Многие играли в эту замечательную игру. Интересный сюжет, хорошая музыка, неплохой геймплей. Только есть пара моментов, которые мне не нравятся. Бег персонажей очень ограниченный, буквально несколько секунд, а запас сил восстанавливается долго. Система начисления опыта не стимулирует брать напарников, потому что опыт распределяется на всех поровну, и лучше бегать одному, чтобы забирать весь опыт себе. Возьмем отладчик и попробуем это исправить.
Нам понадобятся ArtMoney, IDA, Hiew.
В действиях ничего сложного нет, здесь главное результат. Смещения приведены для версии 1.07.
Запускаем IDA, загружаем файл game.exe, ждем пока завершится анализ. Запускаем стартер, убираем настройку «Полноэкранный режим». Запускаем игру в отладчике, загружаем сохранение где уже можно взять напарника. Берем напарника, выходим на карту.
Бег
Смотрим, сколько запаса сил у персонажа. Здесь это 54. Запускаем ArtMoney. Ищем это значение. Тип «С точкой 4 байта». Пробегаем сколько-нибудь и отсеиваем новое значение. Повторяем сколько нужно, у меня сразу осталось одно значение.
Пробуем заморозить, если запас сил при беге восстанавливается, значит нашли правильно. Только надо снять потом. Включаем режим бега. Ставим игру на паузу (пробел).
Ставим на этот адрес брейкпойнт на запись в 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]
.Судя по значениям, там хранится:
[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 раз в секунду.
Скорее всего это зеленые точки, обозначающие путь.
В байтах эта константа записывается так:
4E 1B E8 B4 81 4E 7B 3F
Открываем game.exe в Hiew и переходим на адрес ".73F088".
Других констант с таким значением нет, ссылка на нее есть только в рассмотренном коде. Можно ее поменять на любое значение, которое вам нужно. Я сделал себе в 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.
Ищем это значение. Здесь нужен тип «Целое 4 байта».
Это не исходная переменная, а вычисляемое значение, приведенное к int. Сам опыт хранится во float, но там другое значение, об этом ниже.
Теперь можно с кем-нибудь подраться. Выходить с карты чтобы посмотреть опыт нельзя, перезагружаться тоже, потому что память выделяется заново, и при повторном заходе на карту будут другие адреса. Надо прибавлять в уме. При этом надо учитывать округление. То есть, если за противника дается 5 очков опыта, и в команде 2 персонажа, то на экране будет отображаться опыт 2, но прибавлять надо 2.5 и брать целую часть.
После пары повторений у меня осталось 6 значений, которые изменяются синхронно.
Попробуем поставить брейкпойнты на запись на каждый адрес. Не забываем про «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.Посмотрим значения.
387 — 271 = 116
Можно предположить, что это полученный и потраченный опыт, а текущий рассчитывается как их разность.
У меня игра падала несколько раз, поэтому я ставил брейкпойнт на код и загружал с сохранения. Поэтому тут 116, а не 120. Но это только подтверждает догадку.
Поставим новый брейкпойнт на
[ebx+4]
.Выбираем всю группу и нападаем на противника.
Брейкпойнт срабатывает до того, как это будет видно на экране.
.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
Поставим брейкпойнт на
00591521
и поучаствуем в битве еще раз.fdivr
делит аргумент на st(0)
и результат записывает в st(0)
: st(0) = arg / st(0)
. В st(0)
находится значение [ebp+var_18]
, в котором находится 2 — число персонажей. В [ebp+arg_4]
находится 4.0 — опыт за противника. При выполнении миссии начисление тоже происходит здесь.Также деление на число участников есть выше:
.text:00591324 fdiv [ebp+var_18]
Но я не нашел, когда выполняется этот код. Там трогать не будем.
Теперь через 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
Теперь персонажи и бегают нормально, и запас сил не бесконечный, на заклинания расходуется как обычно, механика игры сохранена. А напарники получают свой независимый опыт, который можно тратить на их развитие.
Поехали!
Комментарии (14)
demoth
13.08.2017 01:16+2Хорошая статья. Рад, что ещё есть те, кто помнят эту замечательную игру. :)
Пара моментов:
1) Патч 1.07 — это неофициальный фанатский патч, он изменяет только ресурсы игры, но не сам движок. Последний официальный патч — 1.06.
2) Можно было бы обойтись без связки ArtMoney + IDA с помощью CheatEngine. Он умеет всё то, что и ArtMoney, но кроме этого ещё умеет отслеживать, какие инструкции обращаются к указанному адресу (на чтение и/или запись). Разумеется, для чего-то сложного IDA выкинуть уже не получится, но вот ArtMoney — запросто.
Maximbl4
13.08.2017 08:33-8В детстве, когда ещё не умел программировать уже ломал с помощью артмани проклятые земли) да и без всяких отладчиков…
michael_vostrikov Автор
13.08.2017 16:14+1Так многие ломали. А начисление опыта например без отладчика не поменять.
Помню, хотел побегать в сетевой игре на сложных картах. Но там всякие зомби и тени, в одиночку не пройти, надо проходить командой и хорошо прокачанными. Набивал опыт на задании с циклопами, прокачивал ближний бой и кислотную магию, но все равно не хватало. Потом сообразил, как через Артмани взломать силу заклинания в конструкторе заклинаний. Сделал себе мощную молнию на 3 цели и прошел везде где еще не был)
MorgenS
13.08.2017 20:17+1Это все очень круто, но все же несколько ломает механику игры :) При равном распределении опыта между персом и напарниками вскоре возникнет дисбаланс.
С разрешения автора утащу ссылочку для размещения на allods.gipat.ru :)michael_vostrikov Автор
13.08.2017 20:47+1Играл на сложном, особого дисбаланса не заметил. Напарники же не переходят в другой мир, новых надо заново прокачивать. Разве что под конец чуть попроще, потому что в целом опыта много дается. А на Гипате очень даже к месту.
Буду рад)
xlenz
13.08.2017 20:51+2Простите за то что не совсем по теме, но, скажите, есть ли более современный аналог этой игры? Прошёл её много раз, но хотелось бы другой сценарий и графу современную.
michael_vostrikov Автор
13.08.2017 23:03+1Не знаю, я за современными играми не слежу. Иногда читаю описания, но как-то ничего не цепляет. Из современных только в Ведьмака играл.
LoadRunner
14.08.2017 09:46аналог
Это «action-rpg с активной паузой»? На ум приходят SW:KotOR, Jade Empire, Mass Effect (первый), Dragon Age. По сравнению с третьими Аллодами — это подходит под «более современный».
tSmoker
14.08.2017 17:03+2...«Плотину надо поднять рычагом. Я его дам. Канал нужно завалить камнем. Камень я не дам»… что-то вспомнилось…
q_styler
Я как будто попал в свой 10й класс. Проклятые земли, артмани!
tangro
Да, знатный флешбек на полтора десятилетия… А ведь до этого были ещё и оригинальные «Аллоды» не испоганненые меилрушыщем.