질(Zeal) 공격속도 계산 과정의 메커니즘(Mechanism)
조회 수 2,927
추천 수 0
*이 글에서 언급되는 *.txt, *.d2, *.cof파일은 모두 디아블로 관련 MPQ파일을 분해해서 얻은 파일들입니다. 이 파일들을 얻는 방법은 추후 다른 강좌글에서 알려드리도록 하겠습니다.
*번역하기가 참 거시기한(...) AnimationRate같은 단어가 많은 탓에 이를 번역하지 않고 그대로 사용하였습니다.
아마존의 질 공격속도 계산을 통해 공격속도표를 만드는 법을 이해해보도록 합시다.
아마존이 페이즈 블레이드 열정(Passion) 룬워드를 사용한다고 가정한 후에, 질 동작을 1프레임 단위로 분석해보도록 하겠습니다.
-1프레임 : 아마존이 몬스터에게 다가갑니다. 하지만 아직 때리지 않았습니다.
0프레임 : 질 스킬을 사용해 몬스터를 공격합니다. 이 때 D2game.dll에서 아마존의 상태를 A1(Attack1)으로 지정합니다. (PlrMode.txt를 참조하세요)
또, D2game.dll은 AMA11HS.cof라는 애니메이션 파일을 불러옵니다.
AMA11HS는 AM(Amazon)A1(Attack1)1HS(One Hand Swinging Weapon)이라는 뜻으로 아마존이 한 손 휘두르는 무기(페이즈 블레이드가 이에 해당됩니다)로 공격했을 때의 애니메이션을 관장하는 파일입니다.
Animdata.d2에서 AMA11HS.cof의 패러미터를 분석해보도록 합시다.
Cof Name AMA11HS
Number of Frame per direction 0x10 (16)
AnimationSpeed = 0x100 (256)
FrameData00=0
FrameData01=0
FrameData02=0
FrameData03=0
FrameData04=0
FrameData05=0
FrameData06=0
FrameData07=0
FrameData08=0
FrameData09=0
FrameData0A=1
FrameData0B=0
FrameData0C=0
FrameData0D=0
FrameData0E=0
FrameData0F=0
FrameData10=0
프레임 데이터(FrameData)는 무기를 휘두르는 일련의 동작이 몇 장의 그림으로 이루어졌는지를 나타내는 나타내는 것입니다. 아마존이 한손무기를 들고 때리는 동작은 모두 FrameData00부터 FrameData0F까지 16장의 그림으로 구성되어있다는 뜻입니다. (FrameData10은 실제로 화면에 보여주지 않지만 형식적으로 존재하는 데이터.)
FrameData0A의 값이 1인 것은 0A번 동작에서 적에게 데미지를 입히는 것을 의미합니다.
PtUnit[+50]은 Animdata.d2안의 AMA11HS.cof의 포인터로써 작동합니다.
D2Common.10436이 몇 번 동작부터 보여줄 것인지를 결정합니다.
PtUnit[+44]=200
아마존이 한손검을 들고 공격을 할 땐 16개의 그림 중 02번(동작이 00번부터 0F번까지 있음) 그림부터 나타납니다.
왜 PtUnit[+44]의 값이 2가 아니라 200일까요? 디아블로는 DWORD값을 이용해 눈에 보이는 수치보다 256배 정밀한 값을 다루기 때문입니다.
450마나 같은 경우에는 450을 십육진수로 표현하면 1C2입니다. 이것이 디아블로의 메모리에서는 00 01 C2 00 이라는 값으로 다루어집니다. 뒤에 붙는 00은 정밀값(Extra-precision)으로써 화면상의 마나수치에선 표현되지 않습니다.
4.5마나 같은 경우에는 00 01 C2라는 값으로 표현되지요. 화면상으로 볼 때는 4마나로 표현되지만 컴퓨터의 메모리는 00 01 C2(4.5)라는 값을 기억하고 있는 것입니다.
따라서 PtUnit[+44]값으로 나온 200은 2프레임 + 00(1/256 프레임 단위)로 이해해야 옳습니다.
D2common.10376은 AnimationRate라는 값을 지정합니다.
AnimationRate = AnimationSpeed * AnimationAcceleration /100
AnimationAccelration = 100 - WSM + EIAS
AnimationSpeed는 Animdata.d2를 추출하게 되면 알 수 있습니다.
WSM은 무기의 기본공격속도, EIAS는 유효공속입니다.
EIAS를 다루는 법은 EIAS↔IAS 변수변환(링크)를 참고하세요.
Weapons.txt에서 알아낸 페이즈블레이드의 WSM은 -30입니다.
IAS(공격 속도 증가)는 25(열정 룬워드의 옵션)입니다. EIAS로 변환하면 20ΔEIAS입니다.
따라서 AnimationAcceleration은 100 - (-30) + 20 = 150입니다.
AMA11HS.cof에서 알아낸 AnimationSpeed는 256이므로 AnimationRate값을 구하면 256*150/100=384입니다. 십육진수로 표현하면 180이 되겠네요.
PtUnit[+4C]는 AnimationRate를 다루는 포인터로써, 여기에 180이란 값을 넣게 되면 메모리에는 00 01 80으로 기록됩니다. 1프레임에 1.5개의 동작이 지나감을 알 수 있습니다.
이제 플레이어의 캐릭터 종류에 따른 변수를 구해야 합니다.
D2common.10349라는 파일은 AnimationLength(정밀값으로 나타나는 FramePerDirection)을 다루는 포인터인 PtUnit[+48]의 값)을 지정합니다. AMA11HS의 FramePerDirection인 16(십육진수로 10)을 정밀값으로 표현하기 위해 256(십육진수로 100)을 곱하면 PtUnit[+48]은 1000(십진수로 4096)이 됩니다.
이 모든게 결정되면 D2client.dll에서 3번째 동작부터 화면에 띄울 것을 명령합니다.
1프레임 : D2common.10375는 포인터인 PtUnit[+48]과 PtUnit[+44]값을 비교합니다. 애니메이션 길이와 애니메이션 시작 프레임을 비교하는 것이지요. 10 00 > 02 00 으로 애니메이션은 끝나지 않았습니다.
D2common.10373은 다음 동작이 일어날 값을 계산하여 PtUnit[+44]에 지정합니다.
PtUnit[+44]=PtUnit[+44]+PtUnit[+4C]=200+180=380
즉 1프레임에는 03번 동작을 화면에 보여줍니다. 디아블로는 정수를 취급하므로 3.5가 아닌 3(소숫점 버림)번 동작이 나타나게 되는 것이지요.
프레임 데이터(FrameData)가 0이면 PtUnit[+44]에 프레임 데이터 카운터(FrameDataCounter)인 PtUnit[+4C]의 값을 더합니다. PtUnit[+44]가 AnimationLength인 1000을 넘으면 애니메이션은 끝이 나게 됩니다.
4번째 동작까지의 프레임 데이터를 보겠습니다. Animdata.d2에서 추출한 AMA11HS.cof를 보면 다음과 같습니다.
FrameData00=0 <- 생략
FrameData01=0 <- 생략
FrameData02=0 <- 공격 0프레임째(스킬을 사용한 순간)에 화면에 나타남
FrameData03=0 <- 공격 1프레임째에 화면에 나타남
결론은 "애니메이션이 아직 끝나지 않았으니 PtUnit[+44]에 PtUnit[+4C]를 더하라"라는 것이군요. 그래서 위에서 PtUnit[+44]값인 200에 PtUnit[+4C]값인 180을 더해 1프레임에는 몇 번째 동작을 보여줘야 하는지를 계산한 것이었습니다.
D2client.dll에서 03번 동작을 보여주도록 명령합니다.
2프레임
D2common.10375는 PtUnit[+48]과 PtUnit[+44]값을 비교해 애니메이션이 끝났는지를 계산합니다.
10 00 > 03 80 이므로 애니메이션은 아직 끝나지 않았습니다.
애니메이션이 끝나지 않았으므로 PtUnit[+44]에 PtUnit[+4C]를 더합니다. 380 + 180 = 500입니다. 2프레임에는 05번 동작을 화면에 보여줍니다.
FrameData04=0 <- 생략
FrameData05=0 <- 공격 2프레임째에 화면에 나타남
프레임 데이터는 위와 같으므로 애니메이션은 아직 끝나지 않습니다.
D2client.dll에서 05번째 동작을 보여주도록 명령합니다.
3프레임
D2common.10375에서 AnimationLength와 PtUnit[+44]를 비교합니다. 1000 > 500 이므로 애니메이션은 아직 끝나지 않았습니다.
PtUnit[+44]는 PtUnit[+4C]를 더해 새로운 값으로 갱신됩니다. 500+180=680이므로 3프레임에는 06번째 동작을 보여주도록 명령합니다.
FrameData06=0 <- 공격 3프레임째에 화면에 나타남
D2client.dll에서 06번 동작을 보여주도록 명령합니다.
4프레임
1000 > 680, 애니메이션 끝나지 않음
FrameData07=0 <- 생략
FrameData08=0 <- 공격 4프레임째에 화면에 나타남
PtUnit[+44]은 680+180=800이라는 값으로 갱신
08번 동작을 보여주도록 명령
5프레임
1000 > 800, 애니메이션 끝나지 않음
FrameData09=0 <- 공격 5프레임째에 화면에 나타남
PtUnit[+44]은 800+180=980이라는 값으로 갱신
09번 동작을 보여주도록 명령
6프레임
1000 > 980, 애니메이션 끝나지 않음
FrameData0A=1 <- 생략
FrameData0B=0 <- 공격 6프레임째에 화면에 나타남
FrameData0A=1이므로 ActionFlag(타격이 일어나는 순간)을 알리는 포인터인 PtUnit[+4E]는 1로 지정됩니다.
FrameData0B=0
D2client.dll은 화면에 0B번 동작을 보여줄 것을 명령합니다.
그리고 D2game.dll을 불러내 타격과 관련된 모든 함수(데미지, 확률로 시전되는 스킬...등등)를 처리합니다.
PtUnit[+44]는 0이 됩니다. Rollback이 이루어지기 때문입니다.
Rollback에 대한 것은 '롤백(Rollback)에 대한 이해'(링크)를 참조하세요.
롤백에 의해 PtUnit[+44] 값이 0이 되었으므로, 똑같은 계산을 반복하도록 합니다.
7프레임 : PtUnit[+44]=180, 01번 동작 보여줌
8프레임 : PtUnit[+44]=300, 03번 동작 보여줌
9프레임 : PtUnit[+44]=480, 04번 동작 보여줌
10프레임 : PtUnit[+44]=600, 06번 동작 보여줌
11프레임 : PtUnit[+44]=780, 07번 동작 보여줌
12프레임 : PtUnit[+44]=900, 09번 동작 보여줌
13프레임 : 롤백 없음. PtUnit[+44]=A80, 0A번 동작 보여줌
13프레임에서 새로운 타격이 일어났습니다. PtUnit[+44]값에서 정밀값의 앞 두자리를 제거하면 A(십진수로 10)가 됩니다. 이 값을 FrameDataActionFlag 라고 명명합니다. 따라서 FrameDataActionFlag는 10입니다.
14프레임 : PtUnit[+44]=C00, 0C번 동작 보여줌
15프레임 : PtUnit[+44]=D80, 0D번 동작 보여줌
16프레임 : PtUnit[+44]=F00, 0F번 동작 보여줌
17프레임 : PtUnit[+44]=1080, 동작 없음
PtUnit[+44]=1080이므로 1000 < 1080, 애니메이션이 끝나게 됩니다.
17프레임에 아무 동작도 보여주지 않는 이유는 디아블로가 애니메이션의 마지막 프레임에는 동작을 보여주지 않기 때문입니다.
애니메이션이 끝난 후에는 D2game.dll에서 아마존의 상태를 새로 지정합니다. 질 스킬을 사용한 후에 달리는 동작을 취한다면 아마존의 상태를 RN(Run)으로 지정하겠죠. 그리고 그에 맞는 cof파일을 불러오는 것입니다.
결론
페이즈 블레이드 열정을 사용하는 아마존이 질 스킬을 사용하면 6프레임, 13프레임에서 타격이 이루어집니다. 그리고 16프레임에서 애니메이션이 끝나게 되지요.
우리는 컴퓨터가 어떻게 동작을 계산하는 지 알았으니, 이를 통해 공식을 구해보도록 하겠습니다.
공식에 필요한 변수는 다음과 같습니다.
(Amindata.d2에서 얻어낸 값입니다)
FramesPerDirection = 16 (Base+1 값으로 알려진 값입니다.)
AnimationSpeed = 256
(Ollydbg로 Diablo.exe를 뜯어내 확인한 값입니다)
StartingFrame = 2
(계산과정에서 알아낸 값입니다)
FrameDataActionFlag = 10
첫번째 타격 = {PtUnit[+48] - StartingFrame / PtUnit[+4C]}-1
풀어쓰면, {AnimationSpeed * (FramesPerDirection - StartingFrame) / AnimationAcceleration} - 1입니다.
{, }괄호 안의 값은 소숫점 올림을 취합니다.
다음 타격 = {AnimationSpeed * FramesPerDirection / AnimationAcceleration} - 1
이제 이 공식이 실제로 맞는지 확인합니다.
첫번째 타격 = {256*14/384}-1=9
다음 타격 = {256*16/384}-1=10
맞지 않네요. 왜냐면 질은 롤백(Rollback) 공격이기 때문입니다. 롤백을 적용한 공식을 구하면 다음과 같습니다.
첫번째 타격 = {AnimationSpeed * (FrameDataActionFlag - StartingFrame) / AnimationRate}
다음 타격 = {AnimationSpeed * FrameDataActionFlag / AnimationRate}
애니메이션 종료 = {AnimationSpeed * FramesPerDirection / AnimationRate} - 1 + 첫번째 타격
이제 이 공식이 실제로 맞는지 확인하면 다음과 같습니다.
첫번째 타격 = {256*(10-2)/384}=6
다음 타격 = {256*10/384}=7
애니메이션 종료 = {256*16/384}-1+6=16
첫번째 타격은 6프레임에 이루어지며 그 다음부터는 7을 더하는 것을 알 수 있습니다. 그리고 16프레임에 애니메이션이 종료됩니다. 실제와도 같군요.
그런데 왜 첫번째 타격, 다음 타격에서는 1을 빼지 않고 애니메이션 종료 프레임을 구할 때만 1을 뺄까요?
디아블로는 애니메이션의 마지막 프레임에는 동작을 보여주지 않기 때문입니다.
참고로, 질 스킬레벨이 1이라 2회 타격만 일어난 것이지 스킬 레벨이 높아져 5회 타격이 일어날 경우에는 네 번의 Rollback이 일어나며 6프레임, 13프레임, 20프레임, 27프레임, 34프레임에서 타격이 일어납니다. 그리고 38프레임에 애니메이션이 끝나게 됩니다.
*번역하기가 참 거시기한(...) AnimationRate같은 단어가 많은 탓에 이를 번역하지 않고 그대로 사용하였습니다.
아마존의 질 공격속도 계산을 통해 공격속도표를 만드는 법을 이해해보도록 합시다.
아마존이 페이즈 블레이드 열정(Passion) 룬워드를 사용한다고 가정한 후에, 질 동작을 1프레임 단위로 분석해보도록 하겠습니다.
-1프레임 : 아마존이 몬스터에게 다가갑니다. 하지만 아직 때리지 않았습니다.
0프레임 : 질 스킬을 사용해 몬스터를 공격합니다. 이 때 D2game.dll에서 아마존의 상태를 A1(Attack1)으로 지정합니다. (PlrMode.txt를 참조하세요)
또, D2game.dll은 AMA11HS.cof라는 애니메이션 파일을 불러옵니다.
AMA11HS는 AM(Amazon)A1(Attack1)1HS(One Hand Swinging Weapon)이라는 뜻으로 아마존이 한 손 휘두르는 무기(페이즈 블레이드가 이에 해당됩니다)로 공격했을 때의 애니메이션을 관장하는 파일입니다.
Animdata.d2에서 AMA11HS.cof의 패러미터를 분석해보도록 합시다.
Cof Name AMA11HS
Number of Frame per direction 0x10 (16)
AnimationSpeed = 0x100 (256)
FrameData00=0
FrameData01=0
FrameData02=0
FrameData03=0
FrameData04=0
FrameData05=0
FrameData06=0
FrameData07=0
FrameData08=0
FrameData09=0
FrameData0A=1
FrameData0B=0
FrameData0C=0
FrameData0D=0
FrameData0E=0
FrameData0F=0
FrameData10=0
프레임 데이터(FrameData)는 무기를 휘두르는 일련의 동작이 몇 장의 그림으로 이루어졌는지를 나타내는 나타내는 것입니다. 아마존이 한손무기를 들고 때리는 동작은 모두 FrameData00부터 FrameData0F까지 16장의 그림으로 구성되어있다는 뜻입니다. (FrameData10은 실제로 화면에 보여주지 않지만 형식적으로 존재하는 데이터.)
FrameData0A의 값이 1인 것은 0A번 동작에서 적에게 데미지를 입히는 것을 의미합니다.
PtUnit[+50]은 Animdata.d2안의 AMA11HS.cof의 포인터로써 작동합니다.
D2Common.10436이 몇 번 동작부터 보여줄 것인지를 결정합니다.
PtUnit[+44]=200
아마존이 한손검을 들고 공격을 할 땐 16개의 그림 중 02번(동작이 00번부터 0F번까지 있음) 그림부터 나타납니다.
왜 PtUnit[+44]의 값이 2가 아니라 200일까요? 디아블로는 DWORD값을 이용해 눈에 보이는 수치보다 256배 정밀한 값을 다루기 때문입니다.
450마나 같은 경우에는 450을 십육진수로 표현하면 1C2입니다. 이것이 디아블로의 메모리에서는 00 01 C2 00 이라는 값으로 다루어집니다. 뒤에 붙는 00은 정밀값(Extra-precision)으로써 화면상의 마나수치에선 표현되지 않습니다.
4.5마나 같은 경우에는 00 01 C2라는 값으로 표현되지요. 화면상으로 볼 때는 4마나로 표현되지만 컴퓨터의 메모리는 00 01 C2(4.5)라는 값을 기억하고 있는 것입니다.
따라서 PtUnit[+44]값으로 나온 200은 2프레임 + 00(1/256 프레임 단위)로 이해해야 옳습니다.
D2common.10376은 AnimationRate라는 값을 지정합니다.
AnimationRate = AnimationSpeed * AnimationAcceleration /100
AnimationAccelration = 100 - WSM + EIAS
AnimationSpeed는 Animdata.d2를 추출하게 되면 알 수 있습니다.
WSM은 무기의 기본공격속도, EIAS는 유효공속입니다.
EIAS를 다루는 법은 EIAS↔IAS 변수변환(링크)를 참고하세요.
Weapons.txt에서 알아낸 페이즈블레이드의 WSM은 -30입니다.
IAS(공격 속도 증가)는 25(열정 룬워드의 옵션)입니다. EIAS로 변환하면 20ΔEIAS입니다.
따라서 AnimationAcceleration은 100 - (-30) + 20 = 150입니다.
AMA11HS.cof에서 알아낸 AnimationSpeed는 256이므로 AnimationRate값을 구하면 256*150/100=384입니다. 십육진수로 표현하면 180이 되겠네요.
PtUnit[+4C]는 AnimationRate를 다루는 포인터로써, 여기에 180이란 값을 넣게 되면 메모리에는 00 01 80으로 기록됩니다. 1프레임에 1.5개의 동작이 지나감을 알 수 있습니다.
이제 플레이어의 캐릭터 종류에 따른 변수를 구해야 합니다.
D2common.10349라는 파일은 AnimationLength(정밀값으로 나타나는 FramePerDirection)을 다루는 포인터인 PtUnit[+48]의 값)을 지정합니다. AMA11HS의 FramePerDirection인 16(십육진수로 10)을 정밀값으로 표현하기 위해 256(십육진수로 100)을 곱하면 PtUnit[+48]은 1000(십진수로 4096)이 됩니다.
이 모든게 결정되면 D2client.dll에서 3번째 동작부터 화면에 띄울 것을 명령합니다.
1프레임 : D2common.10375는 포인터인 PtUnit[+48]과 PtUnit[+44]값을 비교합니다. 애니메이션 길이와 애니메이션 시작 프레임을 비교하는 것이지요. 10 00 > 02 00 으로 애니메이션은 끝나지 않았습니다.
D2common.10373은 다음 동작이 일어날 값을 계산하여 PtUnit[+44]에 지정합니다.
PtUnit[+44]=PtUnit[+44]+PtUnit[+4C]=200+180=380
즉 1프레임에는 03번 동작을 화면에 보여줍니다. 디아블로는 정수를 취급하므로 3.5가 아닌 3(소숫점 버림)번 동작이 나타나게 되는 것이지요.
프레임 데이터(FrameData)가 0이면 PtUnit[+44]에 프레임 데이터 카운터(FrameDataCounter)인 PtUnit[+4C]의 값을 더합니다. PtUnit[+44]가 AnimationLength인 1000을 넘으면 애니메이션은 끝이 나게 됩니다.
4번째 동작까지의 프레임 데이터를 보겠습니다. Animdata.d2에서 추출한 AMA11HS.cof를 보면 다음과 같습니다.
FrameData00=0 <- 생략
FrameData01=0 <- 생략
FrameData02=0 <- 공격 0프레임째(스킬을 사용한 순간)에 화면에 나타남
FrameData03=0 <- 공격 1프레임째에 화면에 나타남
결론은 "애니메이션이 아직 끝나지 않았으니 PtUnit[+44]에 PtUnit[+4C]를 더하라"라는 것이군요. 그래서 위에서 PtUnit[+44]값인 200에 PtUnit[+4C]값인 180을 더해 1프레임에는 몇 번째 동작을 보여줘야 하는지를 계산한 것이었습니다.
D2client.dll에서 03번 동작을 보여주도록 명령합니다.
2프레임
D2common.10375는 PtUnit[+48]과 PtUnit[+44]값을 비교해 애니메이션이 끝났는지를 계산합니다.
10 00 > 03 80 이므로 애니메이션은 아직 끝나지 않았습니다.
애니메이션이 끝나지 않았으므로 PtUnit[+44]에 PtUnit[+4C]를 더합니다. 380 + 180 = 500입니다. 2프레임에는 05번 동작을 화면에 보여줍니다.
FrameData04=0 <- 생략
FrameData05=0 <- 공격 2프레임째에 화면에 나타남
프레임 데이터는 위와 같으므로 애니메이션은 아직 끝나지 않습니다.
D2client.dll에서 05번째 동작을 보여주도록 명령합니다.
3프레임
D2common.10375에서 AnimationLength와 PtUnit[+44]를 비교합니다. 1000 > 500 이므로 애니메이션은 아직 끝나지 않았습니다.
PtUnit[+44]는 PtUnit[+4C]를 더해 새로운 값으로 갱신됩니다. 500+180=680이므로 3프레임에는 06번째 동작을 보여주도록 명령합니다.
FrameData06=0 <- 공격 3프레임째에 화면에 나타남
D2client.dll에서 06번 동작을 보여주도록 명령합니다.
4프레임
1000 > 680, 애니메이션 끝나지 않음
FrameData07=0 <- 생략
FrameData08=0 <- 공격 4프레임째에 화면에 나타남
PtUnit[+44]은 680+180=800이라는 값으로 갱신
08번 동작을 보여주도록 명령
5프레임
1000 > 800, 애니메이션 끝나지 않음
FrameData09=0 <- 공격 5프레임째에 화면에 나타남
PtUnit[+44]은 800+180=980이라는 값으로 갱신
09번 동작을 보여주도록 명령
6프레임
1000 > 980, 애니메이션 끝나지 않음
FrameData0A=1 <- 생략
FrameData0B=0 <- 공격 6프레임째에 화면에 나타남
FrameData0A=1이므로 ActionFlag(타격이 일어나는 순간)을 알리는 포인터인 PtUnit[+4E]는 1로 지정됩니다.
FrameData0B=0
D2client.dll은 화면에 0B번 동작을 보여줄 것을 명령합니다.
그리고 D2game.dll을 불러내 타격과 관련된 모든 함수(데미지, 확률로 시전되는 스킬...등등)를 처리합니다.
PtUnit[+44]는 0이 됩니다. Rollback이 이루어지기 때문입니다.
Rollback에 대한 것은 '롤백(Rollback)에 대한 이해'(링크)를 참조하세요.
롤백에 의해 PtUnit[+44] 값이 0이 되었으므로, 똑같은 계산을 반복하도록 합니다.
7프레임 : PtUnit[+44]=180, 01번 동작 보여줌
8프레임 : PtUnit[+44]=300, 03번 동작 보여줌
9프레임 : PtUnit[+44]=480, 04번 동작 보여줌
10프레임 : PtUnit[+44]=600, 06번 동작 보여줌
11프레임 : PtUnit[+44]=780, 07번 동작 보여줌
12프레임 : PtUnit[+44]=900, 09번 동작 보여줌
13프레임 : 롤백 없음. PtUnit[+44]=A80, 0A번 동작 보여줌
13프레임에서 새로운 타격이 일어났습니다. PtUnit[+44]값에서 정밀값의 앞 두자리를 제거하면 A(십진수로 10)가 됩니다. 이 값을 FrameDataActionFlag 라고 명명합니다. 따라서 FrameDataActionFlag는 10입니다.
14프레임 : PtUnit[+44]=C00, 0C번 동작 보여줌
15프레임 : PtUnit[+44]=D80, 0D번 동작 보여줌
16프레임 : PtUnit[+44]=F00, 0F번 동작 보여줌
17프레임 : PtUnit[+44]=1080, 동작 없음
PtUnit[+44]=1080이므로 1000 < 1080, 애니메이션이 끝나게 됩니다.
17프레임에 아무 동작도 보여주지 않는 이유는 디아블로가 애니메이션의 마지막 프레임에는 동작을 보여주지 않기 때문입니다.
애니메이션이 끝난 후에는 D2game.dll에서 아마존의 상태를 새로 지정합니다. 질 스킬을 사용한 후에 달리는 동작을 취한다면 아마존의 상태를 RN(Run)으로 지정하겠죠. 그리고 그에 맞는 cof파일을 불러오는 것입니다.
결론
페이즈 블레이드 열정을 사용하는 아마존이 질 스킬을 사용하면 6프레임, 13프레임에서 타격이 이루어집니다. 그리고 16프레임에서 애니메이션이 끝나게 되지요.
우리는 컴퓨터가 어떻게 동작을 계산하는 지 알았으니, 이를 통해 공식을 구해보도록 하겠습니다.
공식에 필요한 변수는 다음과 같습니다.
(Amindata.d2에서 얻어낸 값입니다)
FramesPerDirection = 16 (Base+1 값으로 알려진 값입니다.)
AnimationSpeed = 256
(Ollydbg로 Diablo.exe를 뜯어내 확인한 값입니다)
StartingFrame = 2
(계산과정에서 알아낸 값입니다)
FrameDataActionFlag = 10
첫번째 타격 = {PtUnit[+48] - StartingFrame / PtUnit[+4C]}-1
풀어쓰면, {AnimationSpeed * (FramesPerDirection - StartingFrame) / AnimationAcceleration} - 1입니다.
{, }괄호 안의 값은 소숫점 올림을 취합니다.
다음 타격 = {AnimationSpeed * FramesPerDirection / AnimationAcceleration} - 1
이제 이 공식이 실제로 맞는지 확인합니다.
첫번째 타격 = {256*14/384}-1=9
다음 타격 = {256*16/384}-1=10
맞지 않네요. 왜냐면 질은 롤백(Rollback) 공격이기 때문입니다. 롤백을 적용한 공식을 구하면 다음과 같습니다.
첫번째 타격 = {AnimationSpeed * (FrameDataActionFlag - StartingFrame) / AnimationRate}
다음 타격 = {AnimationSpeed * FrameDataActionFlag / AnimationRate}
애니메이션 종료 = {AnimationSpeed * FramesPerDirection / AnimationRate} - 1 + 첫번째 타격
이제 이 공식이 실제로 맞는지 확인하면 다음과 같습니다.
첫번째 타격 = {256*(10-2)/384}=6
다음 타격 = {256*10/384}=7
애니메이션 종료 = {256*16/384}-1+6=16
첫번째 타격은 6프레임에 이루어지며 그 다음부터는 7을 더하는 것을 알 수 있습니다. 그리고 16프레임에 애니메이션이 종료됩니다. 실제와도 같군요.
그런데 왜 첫번째 타격, 다음 타격에서는 1을 빼지 않고 애니메이션 종료 프레임을 구할 때만 1을 뺄까요?
디아블로는 애니메이션의 마지막 프레임에는 동작을 보여주지 않기 때문입니다.
참고로, 질 스킬레벨이 1이라 2회 타격만 일어난 것이지 스킬 레벨이 높아져 5회 타격이 일어날 경우에는 네 번의 Rollback이 일어나며 6프레임, 13프레임, 20프레임, 27프레임, 34프레임에서 타격이 일어납니다. 그리고 38프레임에 애니메이션이 끝나게 됩니다.