Проклятые Земли — Улучшаем бег и опыт с напарниками

в 17:20, , рубрики: evil islands, бег персонажа, опыт напарников, проклятые земли, разработка игр, реверс-инжиниринг, метки: , , ,

Проклятые Земли — Улучшаем бег и опыт с напарниками - 1

Многие играли в эту замечательную игру. Интересный сюжет, хорошая музыка, неплохой геймплей. Только есть пара моментов, которые мне не нравятся. Бег персонажей очень ограниченный, буквально несколько секунд, а запас сил восстанавливается долго. Система начисления опыта не стимулирует брать напарников, потому что опыт распределяется на всех поровну, и лучше бегать одному, чтобы забирать весь опыт себе. Возьмем отладчик и попробуем это исправить.

Нам понадобятся ArtMoney, IDA, Hiew.

В действиях ничего сложного нет, здесь главное результат. Смещения приведены для версии 1.07.

Запускаем IDA, загружаем файл game.exe, ждем пока завершится анализ.
Запускаем стартер, убираем настройку «Полноэкранный режим».
Запускаем игру в отладчике, загружаем сохранение где уже можно взять напарника.
Берем напарника, выходим на карту.
Проклятые Земли — Улучшаем бег и опыт с напарниками - 2

Бег

Смотрим, сколько запаса сил у персонажа. Здесь это 54.
Запускаем ArtMoney. Ищем это значение. Тип «С точкой 4 байта».
Пробегаем сколько-нибудь и отсеиваем новое значение.
Повторяем сколько нужно, у меня сразу осталось одно значение.
Проклятые Земли — Улучшаем бег и опыт с напарниками - 3

Пробуем заморозить, если запас сил при беге восстанавливается, значит нашли правильно. Только надо снять потом.
Включаем режим бега. Ставим игру на паузу (пробел).

Ставим на этот адрес брейкпойнт на запись в IDA. Перед этим обязательно надо сделать 'Pause process', иначе случаются вылеты.
Переключаемся на игру, отправляем персонажа в какую-нибудь точку, снимаем с паузы.

Брейкпойнт срабатывает.

.text:00548315 loc_548315:
.text:00548315                 fld     dword ptr [edi+14h]
.text:00548318                 fld     dword ptr [edi+18h]
.text:0054831B                 fmul    ds:dbl_73F088
.text:00548321                 fsubp   st(1), st
.text:00548323                 fst     dword ptr [edi+14h]
.text:00548326 >               fcomp   ds:flt_73B858
.text:0054832C                 fnstsw  ax
.text:0054832E                 test    ah, 1
.text:00548331                 jz      short loc_548388

Запись происходит в инструкции fst dword ptr [edi+14h]. Можно поставить Operand type — Floating point на этот адрес и на соседний [edi+18h].
Проклятые Земли — Улучшаем бег и опыт с напарниками - 4

Судя по значениям, там хранится:
[edi+14h] — текущее значение
[edi+18h] — максимальное значение

Видно, что максимальный запас сил персонажа умножается на константу dbl_73F088, и результат вычитается из текущего значения. Поэтому все персонажи бегают одинаково.

.rdata:0073F088 dbl_73F088      dq 6.666666666666666e-3
; 6.666666666666666e-3 = 0.006666666666666666 = 1/150

То есть персонаж может пробежать примерно 150 «шагов», но это не совсем шаги, которые видно на анимации, потому что вычитание происходит гораздо чаще. Полный запас сил расходуется за 9-10 секунд, значит вычитание вызывается 15 — 16.66 раз в секунду.
Скорее всего это зеленые точки, обозначающие путь.
Проклятые Земли — Улучшаем бег и опыт с напарниками - 5

В байтах эта константа записывается так:

4E 1B E8 B4 81 4E 7B 3F

Открываем game.exe в Hiew и переходим на адрес ".73F088".
Проклятые Земли — Улучшаем бег и опыт с напарниками - 6

Других констант с таким значением нет, ссылка на нее есть только в рассмотренном коде.

Можно ее поменять на любое значение, которое вам нужно. Я сделал себе в 3 раза меньше.
(1/150)/3 = 1/450 = 0.0022222222222222222

Для конвертации float/double в hex-представление можно воспользоваться онлайн-конвертером, например этим.

для справки

0.0066666666666666667 - 0x3F7B4E81B4E81B4F
0.006666666666666666  - 0x3F7B4E81B4E81B4E
0.0022222222222222222 - 0x3F623456789ABCDF
0.002222222222222222  - 0x3F623456789ABCDE

Получается красивое число 0x3F623456789ABCDF

DF BC 9A 78 56 34 62 3F

Заменяем, сохраняем, запускаем. Вот, так гораздо лучше.

Опыт

Тут немного сложнее. Он не хранится в явном виде. Нужно искать через связанные значения. Начинаем, впрочем, так же.

Смотрим, сколько опыта отображается у персонажа. Это можно сделать в режиме между картами. У меня это 116.
Проклятые Земли — Улучшаем бег и опыт с напарниками - 7

Ищем это значение. Здесь нужен тип «Целое 4 байта».

Это не исходная переменная, а вычисляемое значение, приведенное к int. Сам опыт хранится во float, но там другое значение, об этом ниже.

Теперь можно с кем-нибудь подраться. Выходить с карты чтобы посмотреть опыт нельзя, перезагружаться тоже, потому что память выделяется заново, и при повторном заходе на карту будут другие адреса. Надо прибавлять в уме. При этом надо учитывать округление. То есть, если за противника дается 5 очков опыта, и в команде 2 персонажа, то на экране будет отображаться опыт 2, но прибавлять надо 2.5 и брать целую часть.
Проклятые Земли — Улучшаем бег и опыт с напарниками - 8

После пары повторений у меня осталось 6 значений, которые изменяются синхронно.
Проклятые Земли — Улучшаем бег и опыт с напарниками - 9

Попробуем поставить брейкпойнты на запись на каждый адрес. Не забываем про «Pause process».

Подходит первый адрес. Остальные срабатывают по rep movsd.

.text:00522D00                 fld     dword ptr [ebx+700h]
.text:00522D06                 fadd    dword ptr [ebx+4]
.text:00522D09                 fsub    dword ptr [ebx+8]
.text:00522D0C                 fstp    [ebp+var_10]
.text:00522D0F                 fld     [ebp+var_10]
.text:00522D12                 fistp   [ebp+var_C]
.text:00522D15                 mov     edx, [ebp+var_C]
.text:00522D18                 mov     [edi+8], edx
.text:00522D1B >               mov     eax, [ebx+10h]
.text:00522D1E                 mov     [ebp+var_10], eax

Он срабатывает при беге или при бое любого из персонажей. Поэтому здесь лучше управлять только одним, а не группой, чтобы не путаться. В ebx находится адрес объекта персонажа. В [ebx+700h] находится 0.

Посмотрим значения.
Проклятые Земли — Улучшаем бег и опыт с напарниками - 10

387 — 271 = 116
Можно предположить, что это полученный и потраченный опыт, а текущий рассчитывается как их разность.

Уберем этот брейкпойнт и поставим новый на [ebx+4].

Выбираем всю группу и нападаем на противника.
Проклятые Земли — Улучшаем бег и опыт с напарниками - 11

Брейкпойнт срабатывает до того, как это будет видно на экране.

.text:005239D7                 fld     ds:dbl_73E128
.text:005239DD                 fld     dword ptr [esi+20h]
.text:005239E0                 fsub    ds:flt_73E124
.text:005239E6                 call    __CIpow
.text:005239EB                 fmul    [ebp+arg_4]
.text:005239EE                 fadd    dword ptr [esi+700h]
.text:005239F4                 fcom    ds:flt_73B858
.text:005239FA                 fst     dword ptr [esi+700h]
.text:00523A00                 fnstsw  ax
.text:00523A02                 test    ah, 41h
.text:00523A05                 jnz     short loc_523A19
.text:00523A07                 fadd    dword ptr [esi+4]
.text:00523A0A                 mov     dword ptr [esi+700h], 0
.text:00523A14                 fstp    dword ptr [esi+4]
.text:00523A17 >               jmp     short loc_523A1B

В [esi+4] новое значение опыта. В [ebp+arg_4] число 2.0. За молодого кабана дают 4.0, значит деление находится до вызова функции.

Выходим из функции через Ctrl+F7. Это обертка, выходим еще раз.

Смотрим чуть выше, там находится такой код.

.text:00591521 loc_591521:
.text:00591521                 fild    [ebp+var_18]
.text:00591524                 xor     esi, esi
.text:00591526                 cmp     eax, edi
.text:00591528                 mov     [ebp+var_14], esi
.text:0059152B                 fdivr   [ebp+arg_4]
.text:0059152E                 fstp    [ebp+arg_4]
.text:00591531                 jle     short loc_5915A5
.text:00591533                 jmp     short loc_591537

Также деление на число участников есть выше:

.text:00591324                 fdiv    [ebp+var_18]

Но я не нашел, когда выполняется этот код. Там трогать не будем.

Поставим брейкпойнт на 00591521 и поучаствуем в битве еще раз.
fdivr делит аргумент на st(0) и результат записывает в st(0): st(0) = arg / st(0). В st(0) находится значение [ebp+var_18], в котором находится 2 — число персонажей. В [ebp+arg_4] находится 4.0 — опыт за противника. При выполнении миссии начисление тоже происходит здесь.

Теперь через Hiew можно убрать код для деления. Из-за особенностей fdivr заменяем на nop все 3 команды (9 байт).

// было
.00591521: DB45E8                         fild        d,[ebp][-018]
.00591524: 33F6                           xor         esi,esi
.00591526: 3BC7                           cmp         eax,edi
.00591528: 8975EC                         mov         [ebp][-014],esi
.0059152B: D87D0C                         fdivr       d,[ebp][00C]
.0059152E: D95D0C                         fstp        d,[ebp][00C]
.00591531: 7E72                           jle        .0005915A5
.00591533: EB02                           jmps       .000591537

// стало
.00591521: 909090                         nop
.00591524: 33F6                           xor         esi,esi
.00591526: 3BC7                           cmp         eax,edi
.00591528: 8975EC                         mov         [ebp][-014],esi
.0059152B: 909090                         nop
.0059152E: 909090                         nop
.00591531: 7E72                           jle        .0005915A5
.00591533: EB02                           jmps       .000591537

Теперь персонажи и бегают нормально, и запас сил не бесконечный, на заклинания расходуется как обычно, механика игры сохранена. А напарники получают свой независимый опыт, который можно тратить на их развитие.

Поехали!

Автор: Михаил

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js