Объявления
Поздравляем
Roman2211


Друзья, если не получается зарегистрироваться, напишите на почту vdv_forever@bk.ru.
Я оторву свою задницу от всех дел и обязательно Вас активирую! :smile10:
Добро пожаловать на геройский форум! :smile25:

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

Герои Меча и Магии III: Возрождение Эрафии, Герои Меча и Магии III Дыхание Смерти, Герои Меча и Магии III Клинок Армагеддона, Герои Меча и Магии III Хроники Героев
offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 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)
Поблагодарили: 2185 раз.

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

Сообщение AlexSpl » 03 сен 2017, 15:54

1. Здесь достаточно подключить только хедер патчера: #include "patcher_x86.hpp", т.к. возможности homm3.h не используются.

2.
Цитата:
А почему, кстати, "c->edi + 5" ? В IDA там вот что - "0041D98B movzx cx, [edi+_TownSetup_.x]"

Это x-координата города. Просто в воговской базе определена структура _TownSetup_, в которой есть поле x со смещением 5.

Кстати, movzx cx, byte ptr [edi+5] - беззнаковое расширение байта до слова. Тут надо быть осторожным.
Старшее слово регистра ecx всё равно будет равно нулю после выполнения инструкции and ecx, 3FFh (and ecx, 00000000000000000000001111111111b) ниже по тексту, поэтому присваивать значение можно сразу регистру ecx (а не его младшему слову cx). Но координаты могут быть больше 127 (7Fh), поэтому эту строчку

Код: Выделить всё
c->ecx = *(char*)(c->edi + 5);


нужно заменить на следующую:

Код: Выделить всё
c->ecx = *(unsigned char*)(c->edi + 5);


Иначе вместо, например, 130 получим значение -126 (минус 126).

 
Если интересно, эту команду (movzx cx, byte ptr [edi+5]) затирает джамп LoHook'a HD.HotA:
Код: Выделить всё
0041D98B: count = 1   [LoHook, size: 05, owner: HD.HotA]

Посмотреть можно в файле patcher_x86 dump.txt.


3. Здесь у Вас LoHook на 0x430520, а адрес возврата - 0x430532:

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


Так вы скипуете инструкцию mov ecx, [esi+4Dh] (см. ниже).

Код: Выделить всё
.text:00430520                 sub     eax, 3
.text:00430523                 mov     ecx, [esi+4Dh] ; загрузить в регистр ecx текущее значение MP
.text:00430526                 neg     eax
.text:00430528                 sbb     eax, eax
.text:0043052A                 and     eax, 64h
.text:0043052D                 add     eax, 0C8h
.text:00430532                 sub     ecx, eax
.text:00430534                 mov     [esi+4Dh], ecx


Если пропустить эту инструкцию, герой компа после каста Town Portal будет получать некорректное (очень большое: его адрес (esi) - стоимоcть Town Portal) кол-во MP.

4. Вместо всего этого:

Код: Выделить всё
_PI->WriteHexPatch(0x41D534, "20 03"); // меняем расходы MP на экспертном уровне Магии, с 200 на 800
_PI->WriteHexPatch(0x41D51F, "B0 04"); // меняем расходы MP на основном уровне Магии, с 300 на 1200
...
_PI->WriteLoHook(0x41D52E, advancedEarth); // расходы MP в главной функции заклинания - для продвин. уровня - 1000

можно написать один хук:

Код: Выделить всё
int __stdcall tpCostForHuman(LoHook* h, HookContext* c)
{
   *(int*)(c->ebp - 0x40) = 1200;
   *(int*)(c->ebp - 0x3C) = 1200;
   *(int*)(c->ebp - 0x38) = 1000;
   *(int*)(c->ebp - 0x34) = 800;
   
   return EXEC_DEFAULT;
}

...

_PI->WriteLoHook(0x41D538, tpCostForHuman);


Ну и отформатировать текст DllMain не помешало бы.
Последний раз редактировалось AlexSpl 03 сен 2017, 18:13, всего редактировалось 4 раз(а).
Вернуться к началу

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

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

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

Возникла своеобразная идея о том, как совместить в одном поле Disguise и счетчик для длительности заклинания, и как хранилище выбранной игроком "DisguisePower" (которое в отличие от оригинальной игры может быть больше 3).
По крайней мере, если принять, что длительность будет всегда 2 хода игрока, это возможно.
Выбранную в диалоге игроком DisguisePower мы умножаем на 2 и прибавляем 1. В основной функции (куда хуки писал) данное число (вернее, локальную переменную, куда оно скопировано) мы опять делим на 2, по принципу floor, то есть эта 1 роли не играет.
А при начале хода игрока, проверяем, четное ли число в поле Disguise - если четное, то выставляем в поле -1 - то есть заклинание кончилось, если нечетное - вычитаем 1.

Пока не знаю, будут ли все участки кода игры корректно реагировать на значение в поле Disguise больше 3.
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 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)
Поблагодарили: 2185 раз.

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

Сообщение AlexSpl » 03 сен 2017, 20:36

Менять армию не нужно хотя бы из-за того, что Вы написали:

Цитата:
Что будет, если например, играют 3 игрока, один игрок не в свой ход рассматривает вражеского героя правой кнопкой, и тут на этого героя нападает третий игрок, у которого сейчас ход ?
Разве что запретить такой просмотр (просмотр героев, а не монстров на карте) не в свой ход ?


Достаточно изменить отображаемые значения численности отрядов.

Цитата:
Пока не знаю, будут ли все участки кода игры корректно реагировать на значение в поле Disguise больше 3.

Если я правильно понял, заклинание Disguise в Вашем моде не будет прятать тип отрядов, а только изменять отображаемые значения численности отрядов в армии героя. Поэтому участки кода, ответственные за скрытие типа отряда, можно при желании даже "занопить".

Тогда получается следующая картина. Поле Disguise = -1 - маскировки нет, иначе - Уровень Магии Воздуха (он здесь в регистре edi) <+> Выбранный в диалоге коэффициент <+> Продолжительность заклинания (запаковать эти значения в поле _dword_ disguise можно множеством способов):

Код: Выделить всё
int __stdcall writeDisguiseInfo(LoHook* h, HookContext* c)
{
   // packed_info = Уровень Магии Воздуха (он здесь в регистре edi) <+> Выбранный в диалоге коэффициент <+> Продолжительность заклинания
   *(int*)(c->esi + 270) = packed_info;
   
   return EXEC_DEFAULT;
}

...

_PI->WriteLoHook(0x41C7CC, writeDisguiseInfo);


А уже здесь выстраиваем логику работы с запакованной информацией: 52F45Bh.

После чего останется только реализовать корректное уменьшение длительности заклинания для всех игроков.

* * *
Проверить, реагирует ли AI на Disguise, можно, поставив брейкпоинт на чтение этого поля и пропустив ход.
Вернуться к началу

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

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

Сообщение Ben80 » 04 сен 2017, 14:21

Вот такой код. Виснет, выдает ошибку. В чем причина ?
Если хочется в самом начале битвы выдать сообщение о чем-то (в данном случае о том, что
Герой подсмотрел в книге противника ряд заклинаний), по какому адресу лучше всего вызвать диалог ?

Код: Выделить всё

#include "..\..\include\homm3.h"

Patcher* _P;
PatcherInstance* _PI;


int __stdcall eagleEyeSpell(LoHook* h, HookContext* c)
{
      char Text[200]; // Текстовый буфер
      sprintf(Text, "Битва битв, Дагор Дагоррат");
      CALL_12(void, __fastcall, 0x4F6C00, Text, 1, -1, -1, -1, 0, -1, 0, -1, 0, -1, 0);
   return EXEC_DEFAULT;
}



BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
static _bool_ plugin_On = 0;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
if (!plugin_On)
{
plugin_On = 1;

_P = GetPatcher();
_PI = _P->CreateInstance("HD.Plugin.EagleEye");

_PI->WriteLoHook(0x471F46, eagleEyeSpell);

}
break;

case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 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)
Поблагодарили: 2185 раз.

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

Сообщение AlexSpl » 04 сен 2017, 15:18

У меня Ваш хук работает. Правда, Вы его прямо внутри цикла умудрились поставить, поэтому диалог вызывается несколько раз.

Цитата:
Если хочется в самом начале битвы выдать сообщение о чем-то (в данном случае о том, что
Герой подсмотрел в книге противника ряд заклинаний), по какому адресу лучше всего вызвать диалог ?

Если диалог необходимо показать ещё на карте приключений (после нападения), то можно поставить хук, например, по адресу 0x462652. Если он требуется в самом начале боя - 0x462C7D.
Вернуться к началу

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

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

Сообщение Ben80 » 04 сен 2017, 16:38

Комп у меня действительно глючит, поскольку бэды есть на винте.
Так что не знаю, с чем связать вот уже 2-й раз встречающуюся ситуацию с плагином для заклинания Ослепление -
при попытке применить выдается сообщение "Это заклинание не подействует ни на кого". Артефактов у вражеского героя никаких нет.
Переигрываю сценарий - все нормально работает. В общем, из 15-20 раз пару раз встретил такую ситуацию.
Вернуться к началу

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

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

Сообщение Ben80 » 04 сен 2017, 16:41

Такой вот код (использую Blind для разных тестовых целей):

Код: Выделить всё
int __stdcall blindSpell(LoHook* h, HookContext* c)
{
   if ( c->ebx == 62 )
   {
      int cmAddr = *(int*)0x699420;
      int currentSide = *(int*)(cmAddr + 0x132C0); // Текущая сторона
      int hero = *(int*)(cmAddr + currentSide * 4 + 0x53CC); // Адрес героя, который кастует заклинание, или 0, если героя нет
      int actionId = *(int*)(cmAddr + 0x3C); // ID текущего действия (1 - каст заклинания героем, 2 - передвижение отряда по полю боя и т.п.)
         
      int duration[] = {1, 1, 3, 5};
      if ( actionId == 1 ) c->eax = duration[*(char*)(hero + 201 + 14)];

     char Text[200]; // Текстовый буфер
      //sprintf(Text, "{LoHook @ %08X}\n\nЗначение в поле Disguise %d", h->GetAddress(), *(int*)(hero + 270));
     sprintf(Text, "{LoHook @ %08X}\n\nТекущая сторона: %d", h->GetAddress(), currentSide);
      CALL_12(void, __fastcall, 0x4F6C00, Text, 1, -1, -1, -1, 0, -1, 0, -1, 0, -1, 0);
   }

   return EXEC_DEFAULT;
}
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 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)
Поблагодарили: 2185 раз.

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

Сообщение AlexSpl » 04 сен 2017, 16:44

Потестил хуки в своём предыдущем сообщении. Если нужна музыка, то первый хук нужно ставить раньше, а второй - позже. Дело в том, что при появлении диалога не играет музыка.

Сейчас бы пригодилась воговская база, чтобы не изобретать велосипед. Но у меня IDA предлагает заапгрейдить её, а при попытке это сделать пишет, что база повреждена (database is corrupt). Может, она действительно повреждена?
Последний раз редактировалось AlexSpl 04 сен 2017, 16:49, всего редактировалось 1 раз.
Вернуться к началу

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

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

Сообщение Ben80 » 04 сен 2017, 16:49

А попозже - это например ?
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 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)
Поблагодарили: 2185 раз.

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

Сообщение AlexSpl » 04 сен 2017, 16:50

После того, как запустится тема боя (COMBAT%d.mp3). Попробую поискать функцию.
Последний раз редактировалось AlexSpl 04 сен 2017, 16:51, всего редактировалось 1 раз.
Вернуться к началу

Пред.След.

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

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

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