Объявления

Форум о Героях Меча и Магии и King's Bounty. Если Вы любите эти игры, мы будем рады видеть Вас в наших рядах. :smile2:

Как создать плагин для HD мода

Герои Меча и Магии III: Возрождение Эрафии, Герои Меча и Магии III Дыхание Смерти, Герои Меча и Магии III Клинок Армагеддона, Герои Меча и Магии III Хроники Героев
offlineBen80  
имя: Сергей
Эксперт
Эксперт
 
Сообщения: 1064
Зарегистрирован: 18 июн 2017, 06:49
Пол: Не указан
Поблагодарили: 254 раз.

Re: Как создать плагин для HD мода

Сообщение Ben80 » 17 сен 2017, 18:51

AlexSpl писал(а):

Ну, Wait я уже вернул для PvP, а Defend можно добавить, если патчер получится использовать. Патчить вручную отнимает много времени.


Defend в 20% слабоватый конечно. Хотелось бы повысить разнообразие стратегий, чтобы чаще был резон эту кнопку нажимать. Так что думаю сделать 35%.
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 3634
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 1660 раз.

Re: Как создать плагин для HD мода

Сообщение AlexSpl » 17 сен 2017, 18:59

Цитата:
Defend в 20% слабоватый конечно. Хотелось бы повысить разнообразие стратегий, чтобы чаще был резон эту кнопку нажимать. Так что думаю сделать 35%.

Ставим брейкпоинт на запись значения защиты юнита - и выходим на нужный код.
Вернуться к началу

offlineBen80  
имя: Сергей
Эксперт
Эксперт
 
Сообщения: 1064
Зарегистрирован: 18 июн 2017, 06:49
Пол: Не указан
Поблагодарили: 254 раз.

Re: Как создать плагин для HD мода

Сообщение Ben80 » 17 сен 2017, 19:11

baratorch Вам ответил :lol:
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 3634
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 1660 раз.

Re: Как создать плагин для HD мода

Сообщение AlexSpl » 17 сен 2017, 19:40

baratorch, конечно, большой молодец. Можно только позавидовать его энтузиазму и стамине.
Вернуться к началу

offlineigrik  
Посвященный
Посвященный
 
Сообщения: 83
Зарегистрирован: 14 сен 2017, 12:35
Пол: Не указан
Поблагодарили: 62 раз.

Re: Как создать плагин для HD мода

Сообщение igrik » 18 сен 2017, 09:09

Ben80 писал(а):

Defend в 20% слабоватый конечно. Хотелось бы повысить разнообразие стратегий, чтобы чаще был резон эту кнопку нажимать. Так что думаю сделать 35%.


Если править только бонус, то можно ассамблерно:
Код: Выделить всё
_PI->WriteHexPatch(0x47908E, "90 90 90");   // устанавливаем 3 nop'a
_PI->WriteHexPatch(0x479097, "6B C9 23");   // imul ecx, 23h (35%)


Если захотеть для различных существ разный бонус устроить, то так:
Код: Выделить всё
int __stdcall Y_NewDeffense_Bonus(LoHook* h, HookContext* c)
{
   // изменение коэффициента защиты 20% -> 35%
   // с полным игнорированием выше_идущего кода игры
   int deff_bonus = *(int*)(c->ebx+204) *35 /100;
   if (deff_bonus < 1) deff_bonus = 1;
   c->ecx = deff_bonus;
   return EXEC_DEFAULT;
}

// хук бонуса "в защите"
_PI->WriteLoHook(0x4790C3, Y_NewDeffense_Bonus);
Вернуться к началу

offlineigrik  
Посвященный
Посвященный
 
Сообщения: 83
Зарегистрирован: 14 сен 2017, 12:35
Пол: Не указан
Поблагодарили: 62 раз.

Re: Как создать плагин для HD мода

Сообщение igrik » 18 сен 2017, 15:20

Давно хотел сделать такую вещь: завоз новых артефактов у торговцев артефактами каждую неделю (невыкупленные арты сохраняются)

 Код
Код: Выделить всё
int __stdcall Y_NewWeek(LoHook* h, HookContext* c)
{
   // артефакты у торговца в замке
   if (o_GameMgr->bMarketArt[0] == -1) o_GameMgr->bMarketArt[0] = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 2);
   if (o_GameMgr->bMarketArt[1] == -1) o_GameMgr->bMarketArt[1] = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 2);
   if (o_GameMgr->bMarketArt[2] == -1) o_GameMgr->bMarketArt[2] = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 2);
   if (o_GameMgr->bMarketArt[3] == -1) o_GameMgr->bMarketArt[3] = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 4);
   if (o_GameMgr->bMarketArt[4] == -1) o_GameMgr->bMarketArt[4] = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 4);
   if (o_GameMgr->bMarketArt[5] == -1) o_GameMgr->bMarketArt[5] = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 4);
   if (o_GameMgr->bMarketArt[6] == -1) o_GameMgr->bMarketArt[6] = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 8);

   // артефакты у торговцев на карте приключений
   if (o_GameMgr->bMarketOnMap_first != 0 ){
      for (int i = o_GameMgr->bMarketOnMap_first; i <= o_GameMgr->bMarketOnMap_last; i += 28){
         if (*(int*)(i + 0)  == -1) *(int*)(i + 0)  = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 2);
         if (*(int*)(i + 4)  == -1) *(int*)(i + 4)  = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 2);
         if (*(int*)(i + 8)  == -1) *(int*)(i + 8)  = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 2);
         if (*(int*)(i + 12) == -1) *(int*)(i + 12) = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 4);
         if (*(int*)(i + 16) == -1) *(int*)(i + 16) = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 4);
         if (*(int*)(i + 20) == -1) *(int*)(i + 20) = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 4);
         if (*(int*)(i + 24) == -1) *(int*)(i + 24) = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 8);
      }
   }
    return EXEC_DEFAULT;
}

Код: Выделить всё
_PI->WriteLoHook(0x4C8446, Y_NewWeek);


в структуре "struct _GameMgr_" в файле "homm3.h"
меняем это:
Код: Выделить всё
_byte_ f1F644[84]; // +1F644h

на это:
Код: Выделить всё
 _byte_ f1F644[32]; // +1F644h

 // артефакты у торговца в замках
 _dword_ bMarketArt[7]; // +1F664h

// что-то с торговцами на карте (возможно указатель на адрес в save/load)
 _dword_ f1F680; // +1F680h

 // первый торговец артефактами на карте в списке
 _dword_ bMarketOnMap_first; // +1F684h

  // последний торговец артефактами на карте в списке
 _dword_ bMarketOnMap_last;  // +1F688h

 _byte_ f1F68C[12]; // +1F68Ch 


upd: дополнил код торговцами на карте приключений
Последний раз редактировалось igrik 19 сен 2017, 06:41, всего редактировалось 2 раз(а).
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 3634
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 1660 раз.

Re: Как создать плагин для HD мода

Сообщение AlexSpl » 19 сен 2017, 01:52

Решил наконец разобраться с этим "странным умножением", тем более, что в коде игры мне оно встречается уже не в первый раз:

Код: Выделить всё
.text:0047907A mov     ecx, [ebx+204]                  ; _BattleStack_* currentStack = (_BattleStack_*)c->ebx;
.text:0047907A                                         ; c->ecx = currentStack->creature.defence; // правильно "defense", кстати
.text:00479080 mov     eax, 51EB851Fh                  ; вот здесь самое интересное: c->eax = 0x51EB851F; // какое-то "странное число" :)
.text:00479085 mov     [ebp+var_4], edi                ; иррелевантно для нашего примера
.text:00479088 mov     edi, [ebx+132]                  ; c->edi = currentStack->creature.flags;
.text:0047908E lea     ecx, [ecx+ecx*4]                ; c->ecx = c->ecx + c->ecx * 4; // т.е. c->ecx *= 5;
.text:00479091 or      edi, 8000000h                   ; c->edi |= 0x80000000; // устанавливаем флаг Defend в c->edi
.text:00479097 shl     ecx, 2                          ; c->ecx <<= 2; // экивалентно c->ecx *= 4;
.text:00479097                                         ; итого, мы умножили c->ecx на 20: сначала на 5 (см. выше), а потом на 4
.text:0047909A imul    ecx                             ; умножаем c->eax на c->ecx, т.е. "странное число" на (20 * защита_стека),
.text:0047909A                                         ; произведение - в паре регистров edx:eax
.text:0047909C sar     edx, 5                          ; edx >>= 5; // эквивалентно c->edx /= 32;
.text:0047909C                                         ; (арифметический сдвиг sar вместо shr здесь нужен, чтобы сохранить знак)
.text:0047909F mov     eax, edx                        ; c->eax = c->edx; // внезапно c->eax становится равным (20 * защита_стека) / 100
.text:0047909F                                         ; (см. объяснение, почему так происходит, после кода)
.text:004790A1 mov     [ebx+132], edi                  ; currentStack->creature.flags = c->edi; // пишем обновлённые флаги
.text:004790A1                                         ; из c->edi обратно в структуру стека
.text:004790A7 shr     eax, 31                         ; c->eax >>= 31; // получаем знак произведения: c->eax = старшему (31-му) биту,
.text:004790A7                                         ; т.е. c->eax = 0, если c->eax положительное число и 1, если отрицательное
.text:004790AA mov     edi, 1                          ; c->edi = 1;
.text:004790AF add     edx, eax                        ; c->edx += c->eax; // корректируем результат: добавляем "знак" (см. выше)
                                                       ; из-за особенностей дополнительного кода
.text:004790B1 cmp     edx, edi                        ; сравниваем c->edx с единицей (1)
.text:004790B3 mov     [ebp+var_1C], edi               ; сохраняем единицу (1) в локальной переменной var_1C
.text:004790B6 mov     [ebp+var_18], edx               ; сохраняем c->edx (произведение) в локальной переменной var_18
.text:004790B9 lea     eax, [ebp+var_1C]               ; c->eax = &var_1C; // т.е. в c->eax - указатель на ячейку памяти,
.text:004790B9                                         ; в которой хранится число 1
.text:004790BC jl      short loc_4790C1                ; if ( c->edx < 1 ) goto loc_4790C1;
.text:004790BE lea     eax, [ebp+var_18]               ; иначе c->eax = &var_18; // т.е. c->eax - указатель на ячейку памяти,
.text:004790BE                                         ; в которой хранится произведение
.text:004790C1
.text:004790C1 loc_4790C1:
.text:004790C1 mov     ecx, [eax]                      ; c->ecx = (int&)c->eax; // или привычней: c->ecx = *(int*)c->eax;
.text:004790C3 mov     eax, [ebx+76]                   ; иррелевантно
.text:004790C6 cmp     eax, edi                        ; для нашего
.text:004790C8 mov     eax, [ebx+52]                   ; примера
.text:004790CB mov     [ebx+1244], ecx                 ; currentStack->creature.defend_bonus = c->ecx;


"Странное число" - это результат оптимизации кода: n / 100 = n * 0.01 = n * 0.32 / 32 = (n * 0.32 * 2^32) / (32 * 2^32) = (n * 1374389534.72) / (2^5 * 2^32) ~ (n * 1374389535) / 2^37 (или n * 0x51EB851F >> 37 для целых неотрицательных n).

 Атака в лоб
Давайте посмотрим, чем интересно это "странное число" 0x51EB851F. Заметим, что при умножении этого "странного числа" на 20 * d (0x14 * d) получается довольно симпатичное число:

0x51EB851F * 0x14 * d = 0x66666666C * d, где d - произвольное число (необязательно целое).

Далее следует десятеричная система счисления и алгебраическая запись (знак умножения отсутствует, где не нужен явно; деление НЕ целочисленное; ^ - возведение в степень):

0x66666666C * d = 6d(16^8 + 16^7 + 16^6 + 16^5 + 16^4 + 16^3 + 16^2 + 16) + 12d

Сумма геометрической прогрессии 16^8 + 16^7 + 16^6 + 16^5 + 16^4 + 16^3 + 16^2 + 16 = (16^8 * 16 - 16)/(16 - 1) = 16(16^8 - 1)/15,
поэтому 0x66666666C * d = 6d * 16(16^8 - 1)/15 + 12d = 32d(16^8 - 1)/5 + 12d = 32d * 16^8/5 - 32d/5 + 12d = 32 * d/5 * 16^8 + 28 * d/5

Далее, d/5 = [d/5] + {d/5}, где [] - целая часть числа, а {} - дробная. Тогда:
32 * ([d/5] + {d/5}) * 16^8 + 28 * d/5 = 32 * [d/5] * 16^8 + 32 * {d/5} * 16^8 + 28 * d/5

Ничего не замечаете? ;) В выражении фигурирует нужное значение [d/5], но как его оттуда достать?

Теперь начинаются фокусы машинной арифметики. Результат умножения 0x51EB851F * 0x14 * d находится в паре регистров edx:eax (в edx - старшие четыре байта результата, в eax - младшие). Другими словами, 0x51EB851F * 0x14 * d = edx * 16^8 + eax. Казалось бы, бери значение edx, дели его на 32 и получай результат :) Самое интересное, что так и надо сделать:

(32 * [d/5] * 16^8 + 32 * {d/5} * 16^8 + 28 * d/5) / 32 = [d/5] * 16^8 + {d/5} * 16^8 + 7d/40

Cделаем замену d = n / 20:

[d/5] * 16^8 + {d/5} * 16^8 + 7d/40 = [n/100] * 16^8 + {n/100} * 16^8 + 7n/800

Получили, что 0x51EB851F * n / 32 = [n/100] * 16^8 + {n/100} * 16^8 + 7n/800. Разделим обе части равенства на 16^8:

0x51EB851F * n / 32 / 16^8 = [n/100] + {n/100} + 7n/(800 * 16^8)

До тех пор, пока справедливо неравенство {n/100} + 7n/(800 * 16^8) < 1, справедливо и равенство [0x51EB851F * n / 32 / 16^8] = [n/100]. Для целых n <= UINT_MAX оно справедливо: max({n/100} + 7n/(800 * 16^8)) = 0.99 + 7(2^32 - 1)/(800 * 16^8) < 1


Поэтому, разделив значение в регистре edx нацело на 32, или сдвинув его значение вправо на 5 разрядов (edx по определению целое), мы получим [d/5], или [20d/100].

Итак, если сдвинуть результат произведения "странного числа" 0x51EB851F и целого неотрицательного числа n на 37 (5 + 32) разрядов вправо (или разделить нацело на 2^37), то получим результат целочисленного деления n на 100:

0x51EB851F * n >> 37 == n / 100

Для отрицательных чисел формула будет чуть сложнее из-за дополнительного кода.


UPD Добавил доказательство. Исправил ошибки в выкладках.
Вернуться к началу

offlineigrik  
Посвященный
Посвященный
 
Сообщения: 83
Зарегистрирован: 14 сен 2017, 12:35
Пол: Не указан
Поблагодарили: 62 раз.

Re: Как создать плагин для HD мода

Сообщение igrik » 20 сен 2017, 06:43

AlexSpl, шикарный разбор :smile20: . Спасибо. Для непрограммиста (как я) такая информация очень ценна. Наконец-то я понял что такое сдвиг и как он работает.

Также я увидел что вы нопаете так (в "HD.Plugin.TownPortal"):
Код: Выделить всё
_PI->WriteHexPatch(0x41D6D1, "90 90 90 90 90 90 90 90 90 90"); // устанавливаем 10 nop'ов

Удобнее это делать так:
Код: Выделить всё
_PI->WriteCodePatch(0x41D6D1, "%n", 10);   // устанавливаем 10 nop'ов
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 3634
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 1660 раз.

Re: Как создать плагин для HD мода

Сообщение AlexSpl » 20 сен 2017, 11:09

Вообще, согласно мануалу от Intel, последовательность "нопов" следует заменять на:

Цитата:
3.5.1.10 Using NOPs
Code generators generate a no-operation (NOP) to align instructions. Examples of NOPs of different
lengths in 32-bit mode are shown below:
1-byte: XCHG EAX, EAX
2-byte: 66 NOP
3-byte: LEA REG, 0 (REG) (8-bit displacement)
4-byte: NOP DWORD PTR [EAX + 0] (8-bit displacement)
5-byte: NOP DWORD PTR [EAX + EAX*1 + 0] (8-bit displacement)
6-byte: LEA REG, 0 (REG) (32-bit displacement)
7-byte: NOP DWORD PTR [EAX + 0] (32-bit displacement)
8-byte: NOP DWORD PTR [EAX + EAX*1 + 0] (32-bit displacement)
9-byte: NOP WORD PTR [EAX + EAX*1 + 0] (32-bit displacement)
These are all true NOPs, having no effect on the state of the machine except to advance the EIP. Because
NOPs require hardware resources to decode and execute, use the fewest number to achieve the desired
padding.
The one byte NOP:[XCHG EAX,EAX] has special hardware support. Although it still consumes a µop and
its accompanying resources, the dependence upon the old value of EAX is removed. This µop can be
executed at the earliest possible opportunity, reducing the number of outstanding instructions and is the
lowest cost NOP.
The other NOPs have no special hardware support. Their input and output registers are interpreted by the
hardware. Therefore, a code generator should arrange to use the register containing the oldest value as
input, so that the NOP will dispatch and release RS resources at the earliest possible opportunity.

GENERAL OPTIMIZATION GUIDELINES
Try to observe the following NOP generation priority:
• Select the smallest number of NOPs and pseudo-NOPs to provide the desired padding.
• Select NOPs that are least likely to execute on slower execution unit clusters.
• Select the register arguments of NOPs to reduce dependencies.


Но тогда получится каша из инструкций, уж лучше байтом 90 (xchg eax, eax), чтобы легко было читать.

* * *
Вот ещё из одного мануала от Intel табличка:

Цитата:
Table 4-12. Recommended Multi-Byte Sequence of NOP Instruction
Length Assembly Byte Sequence
2 bytes 66 NOP 66 90H
3 bytes NOP DWORD ptr [EAX] 0F 1F 00H
4 bytes NOP DWORD ptr [EAX + 00H] 0F 1F 40 00H
5 bytes NOP DWORD ptr [EAX + EAX*1 + 00H] 0F 1F 44 00 00H
6 bytes 66 NOP DWORD ptr [EAX + EAX*1 + 00H] 66 0F 1F 44 00 00H
7 bytes NOP DWORD ptr [EAX + 00000000H] 0F 1F 80 00 00 00 00H
8 bytes NOP DWORD ptr [EAX + EAX*1 + 00000000H] 0F 1F 84 00 00 00 00 00H
9 bytes 66 NOP DWORD ptr [EAX + EAX*1 + 00000000H] 66 0F 1F 84 00 00 00 00 00H


Код: Выделить всё
_PI->WriteHexPatch(0x41D6D1, "90 90 90 90 90 90 90 90 90 90");

Так что забивать, наверное, лучше так:
Код: Выделить всё
_PI->WriteHexPatch(0x41D6D1, "90 66 0F 1F 84 00 00 00 00 00");

Но в этом нет особого смысла, важнее читабельность :smile1: Я даже иногда в начало длинной последовательности "нопов" вставляю безусловный переход, чтобы в режиме блок-схем в IDA было всё красиво:
Код: Выделить всё
_PI->WriteHexPatch(0x41D6D1, "EB 08 90 90 90 90 90 90 90 90");
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 3634
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 1660 раз.

Re: Как создать плагин для HD мода

Сообщение AlexSpl » 22 сен 2017, 22:25

Плагин, изменяющий длительность заклинаний, который я написал для FfuzzyLogik.

 
Код: Выделить всё
#define _CRT_SECURE_NO_WARNINGS
#include "HotA\homm3.h"

Patcher* _P;
PatcherInstance* _PI;
static _bool_ plugin_On = 0;

#define DEBUG
#define H_QUICKSAND 0x5A0787
#define H_FORCEFIELD 0x5A0AE2
#define H_FIREWALL 0x5A0C1D

const int hookAddr[] = {H_QUICKSAND, H_FORCEFIELD, H_FIREWALL};
int elemSplDuration[2][21];

#ifdef DEBUG
int showDebugInfo(_ptr_ hAddr, _Hero_* hero, int idSpell, int duration, int n, int artBonus)
{
    sprintf(o_TextBuffer, "{LoHook @ %08X}\n\n%s casts %s\nDuration = %d\nSpell Power = %d\nKnowledge = %d\nFormula: 1 + (2S + K) / %d\nBonus for Artifacts = %d",
        hAddr, hero->name, (o_Spell + idSpell)->name, duration, hero->power, hero->knowledge, n, artBonus);
    CALL_12(void, __fastcall, 0x4F6C00, o_TextBuffer, 1, -1, -1, -1, 0, -1, 0, -1, 0, -1, 0);   
   
    return 0;
}
#endif

int getArtBonus(_Hero_* hero)
{
    int artBonus = 0;

    if ( hero->DoesWearArtifact(AID_RING_OF_THE_MAGI) ) artBonus = 6; else {
        if ( hero->DoesWearArtifact(AID_COLLAR_OF_CONJURING) ) ++artBonus;
        if ( hero->DoesWearArtifact(AID_RING_OF_CONJURING) ) ++artBonus;
        if ( hero->DoesWearArtifact(AID_CAPE_OF_CONJURING) ) artBonus += 2;
    }

    return artBonus;
}

int getActionId()
{
    return *(int*)o_BattleMgr->Offset(0x3C);
}

int __stdcall splCommon(LoHook* h, HookContext* c)
{
    int n = 0;
    _Hero_* hero = (_Hero_*)o_BattleMgr->hero[o_BattleMgr->current_side];

    if ( getActionId() == 1 )
    {
        switch ( c->ebx )
        {
            case SPL_SHIELD:
            case SPL_AIR_SHIELD:
            case SPL_MAGIC_MIRROR:
            case SPL_CURSE:
            case SPL_PRECISION:
            case SPL_WEAKNESS:
            case SPL_SORROW:
            case SPL_SLOW:
            case SPL_FORGETFULNESS:
                n = 6;
                break;

            case SPL_FIRE_SHIELD:
            case SPL_ANTI_MAGIC:
            case SPL_PRAYER:
            case SPL_COUNTERSTRIKE:
                n = 4;
                break;

            case SPL_PROTECTION_FROM_AIR:
            case SPL_PROTECTION_FROM_FIRE:
            case SPL_PROTECTION_FROM_WATER:
            case SPL_PROTECTION_FROM_EARTH:
            case SPL_BLESS:
            case SPL_BLOODLUST:
            case SPL_STONE_SKIN:
            case SPL_MIRTH:
            case SPL_FORTUNE:
            case SPL_MISFORTUNE:
            case SPL_HASTE:
            case SPL_SLAYER:
                n = 3;
                break;

            case SPL_FRENZY:
                n = 8;
                break;

            case SPL_HYPNOTIZE:
            case SPL_BLIND:
                n = 10;
        }
    }

    if ( n ) {
        int artBonus = getArtBonus(hero);
        c->eax = 1 + (2 * hero->power + hero->knowledge) / n + artBonus;

        #ifdef DEBUG
        showDebugInfo(h->GetAddress(), hero, c->ebx, c->eax, n, artBonus);
        #endif
    }

    return EXEC_DEFAULT;
}

int __stdcall splObsctacle(LoHook* h, HookContext* c)
{
    if ( getActionId() == 1 )
    {
        int n = 1;
        switch ( h->GetAddress() )
        {
            case H_QUICKSAND:
                n = 3;
                break;
           
            case H_FORCEFIELD:
                n = 10;
                break;

            case H_FIREWALL:
                n = 8;
        }
       
        _Hero_* hero = (_Hero_*)o_BattleMgr->hero[o_BattleMgr->current_side];
        int artBonus = getArtBonus(hero);
        int duration = 1 + (2 * hero->power + hero->knowledge) / n + artBonus;

        *(int*)(c->ebp - 0x4C) = duration;
       
        #ifdef DEBUG       
        showDebugInfo(h->GetAddress(), hero, *(int*)(c->ebp + 8), duration, n, artBonus);
        #endif
    }
   
    return EXEC_DEFAULT;
}

int __stdcall removeElemental(LoHook* h, HookContext* c)
{
    // int currentRound = *(int*)o_BattleMgr->Offset(0x132A0) - 30000;
    _BattleStack_* currentStack = *(_BattleStack_**)(c->ebp - 4);
   
    if ( elemSplDuration[currentStack->side][currentStack->index_on_side] )
    {
        if ( !--elemSplDuration[currentStack->side][currentStack->index_on_side] ) currentStack->Die(TRUE);
    }

    return EXEC_DEFAULT;
}

int __stdcall splElemental(LoHook* h, HookContext* c)
{
    _BattleStack_* currentStack = (_BattleStack_*)c->eax;

    if ( currentStack && getActionId() == 1 )
    {
        int n = 6;
        _Hero_* hero = (_Hero_*)o_BattleMgr->hero[o_BattleMgr->current_side];
        int artBonus = getArtBonus(hero);
        int duration = 1 + (2 * hero->power + hero->knowledge) / n + artBonus;
        elemSplDuration[currentStack->side][currentStack->index_on_side] = duration;
       
        #ifdef DEBUG
        showDebugInfo(h->GetAddress(), hero, *(int*)(c->ebp + 8), duration, n, artBonus);
        #endif
    }

    return EXEC_DEFAULT;
}

int __stdcall clearElemSpellDuration(LoHook* h, HookContext* c)
{
    memset(&elemSplDuration, 0, sizeof(elemSplDuration));

    return EXEC_DEFAULT;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    if ( DLL_PROCESS_ATTACH == ul_reason_for_call )
    {
        if ( !plugin_On )
        {
            plugin_On = 1;
            _P = GetPatcher();
            _PI = _P->CreateInstance("HD.Plugin.FfuzzyLogikPlugin");
           
            for (int i = 0; i < 3; ++i) _PI->WriteLoHook(hookAddr[i], splObsctacle);
            _PI->WriteLoHook(0x44467E, splCommon);
            _PI->WriteLoHook(0x5A7605, splElemental);
            _PI->WriteLoHook(0x475A3D, removeElemental);
            _PI->WriteLoHook(0x462C7D, clearElemSpellDuration);
        }
    }

    return TRUE;
}

Длительность обычных заклинаний (1 round/spell power) изменяется по формуле 1 + (2 * power + knowledge) / n (см. код). По этой же формуле вычисляется длительность Quicksand, Force Field, Fire Wall, Frenzy, Fire/Earth/Water/Air Elemental.

Плагин FfuzzyLogikBeta
Debug версия плагина

UPD Теперь продолжительность жизни элементалов больше не пишется в полe cost.wood.
Вернуться к началу

Пред.След.

Вернуться в Общий раздел

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1

cron