Объявления

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

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

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

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

Сообщение Ben80 » 06 фев 2019, 16:57

as239 писал(а):

Я тоже хотел оттуда вытащить, но не нашел исходники.


Исходники программ такого уровня, как SoD_SP, на дороге, как правило, не валяются.
Далеко не все выкладывают в свободный доступ код актуальных модов.
Вернуться к началу

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

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

Сообщение AlexSpl » 06 фев 2019, 17:42

Код для Учёного без учёта характеристик героя: навыков, наличия книги и т.п. Полагаю, именно это было нужно, а не то, что герой реально получит?

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

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

char TxtBuffer[50];

int __stdcall showScholarInfo(LoHook* h, HookContext* c)
{
    // Учёный?
    if ( c->eax == 0x51 ) {
        int ScholarType = (*(int*)c->ebx << 0x1D) >> 0x1D;
        switch ( ScholarType ) {
            case 0:
                sprintf(TxtBuffer, "%s %s", (char*)c->edi, "(stat)");
                break;
            case 1:
                sprintf(TxtBuffer, "%s %s", (char*)c->edi, "(skill)");
                break;
            case 2:
                sprintf(TxtBuffer, "%s %s", (char*)c->edi, "(cast)");
                break;
        }

        c->edi = (int)TxtBuffer;
    }

    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.Scholar");

            _PI->WriteLoHook(0x40D059, showScholarInfo);
        }
    }

    return TRUE;
}


UPD: Добавил пропущенную проверку на ID объекта.
Вернуться к началу

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

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

Сообщение AlexSpl » 06 фев 2019, 19:15

Такой маленький tip: чтобы получить адрес инструкции, которая записывает строку в текстовый буфер (o_TextBuffer), нужно поставить вот такой брейкпоинт:

Изображение

Например, так я нашёл место, куда ставить хук.

* * *
Обновил код для отображения кол-ва потерь (добавил пропущенный хук для дистанционной атаки, учёл пожелание насчёт отображения диапазона):

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

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

int MinKilled, MaxKilled, FullHealth;

int __stdcall getLosses(LoHook* h, HookContext* c)
{
    _BattleStack_* foeStack = *(_BattleStack_**)(c->ebp + 8);
           
    MinKilled = c->edi / foeStack->full_hp;
    if ( c->edi % foeStack->full_hp >= foeStack->full_hp - foeStack->lost_hp ) ++MinKilled;
    if ( MinKilled > foeStack->count_current ) MinKilled = foeStack->count_current;
           
    MaxKilled = c->eax / foeStack->full_hp;
    if ( c->eax % foeStack->full_hp >= foeStack->full_hp - foeStack->lost_hp ) ++MaxKilled;
    if ( MaxKilled > foeStack->count_current ) MaxKilled = foeStack->count_current;

    return EXEC_DEFAULT;
}

int __stdcall showLosses(LoHook* h, HookContext* c)
{
    if ( MinKilled == MaxKilled ) sprintf(o_TextBuffer, "%s.  Losses: %d", o_TextBuffer, MaxKilled); else
        sprintf(o_TextBuffer, "%s.  Losses: %d-%d", o_TextBuffer, MinKilled, MaxKilled);
    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.ShowLosses");

            _PI->WriteLoHook(0x493053, getLosses);
            _PI->WriteLoHook(0x492619, showLosses);
            _PI->WriteLoHook(0x492848, showLosses);
        }
    }

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

offlineRoseKavalier  
Мастер
Мастер
 
Сообщения: 331
Зарегистрирован: 23 сен 2017, 17:00
Пол: Не указан
Поблагодарили: 233 раз.

Re: Пользовательские плагины для HD мода

Сообщение RoseKavalier » 07 фев 2019, 05:10

igrik писал(а):

Автор: igrik

Название: защита артефактов, пандор и свитков
Описание: теперь артефакты, ящики пандор и свитки защищены рядом стоящими монстрами
(монстр защищает объекты согласно красной рамки):
Совместимость: SoD, ERA

Изображение

 код
Код: Выделить всё
// есть ли стража рядом с объектом (возвращает координаты клетки с монстрами)
_int_ isGuardNearTheObject(_AdvMgr_* advMng, _int_ xyz)
{
   int xd = b_unpack_x(xyz);
   int yd = b_unpack_y(xyz);
   int z = b_unpack_z(xyz);
   int x, y;
   int size = advMng->map->size;
   // ряд на уровне арта --------------------------------------------------------------------------------------
   x = xd -1; y = yd;
   if ( x >= 0 && x < size && y >= 0 && y < size) {
      if (advMng->map->GetItem(x,y,z)->object_type == 54) {
            return b_pack_xyz(x,y,z); } }

   x = xd +1; y = yd;
   if ( x >= 0 && x < size && y >= 0 && y < size) {
      if (advMng->map->GetItem(x,y,z)->object_type == 54) {
            return b_pack_xyz(x,y,z); } }
   // ряд ниже арта --------------------------------------------------------------------------------------------
   x = xd -1; y = yd +1;
   if ( x >= 0 && x < size && y >= 0 && y < size) {
      if (advMng->map->GetItem(x,y,z)->object_type == 54) {
            return b_pack_xyz(x,y,z); } }

   x = xd;   y = yd +1;
   if ( x >= 0 && x < size && y >= 0 && y < size) {
      if (advMng->map->GetItem(x,y,z)->object_type == 54) {
            return b_pack_xyz(x,y,z); } }

   x = xd +1; y = yd +1;
   if ( x >= 0 && x < size && y >= 0 && y < size) {
      if (advMng->map->GetItem(x,y,z)->object_type == 54) {
            return b_pack_xyz(x,y,z); } }

   // ряд выше арта --------------------------------------------------------------------------------------------     
   x = xd -1; y = yd -1;
   if ( x >= 0 && x < size && y >= 0 && y < size) {
      if (advMng->map->GetItem(x,y,z)->object_type == 54) {
            return b_pack_xyz(x,y,z); } }

   x = xd; y = yd -1;
   if ( x >= 0 && x < size && y >= 0 && y < size) {
      if (advMng->map->GetItem(x,y,z)->object_type == 54) {
            return b_pack_xyz(x,y,z); } }

   x = xd +1; y = yd -1;
   if ( x >= 0 && x < size && y >= 0 && y < size) {
      if (advMng->map->GetItem(x,y,z)->object_type == 54) {
            return b_pack_xyz(x,y,z); } }
   // -----------------------------------------------------------------------------------------------------
   return 0; // монстров вокруг клетки нет
}

// при движении мыши на КП
_int_ __stdcall Y_GetCursorFrameOnAdvMap(HiHook* hook, _AdvMgr_* advMng, _MapItem_* obj)
{
   if ( obj->object_type == 5 || obj->object_type == 6 || obj->object_type == 93 ) { // если объект под мышью артефакт(5), пандора (6) или свиток (93)
      if ( isGuardNearTheObject(advMng, advMng->pack_xyz) ) {
         return 5; // возвратить тип курсора (атакующий меч)
      }
   }
   // если монстров вокруг артефакта нет выполняем стандартную функцию
   return CALL_2(_int_, __thiscall, hook->GetDefaultFunc(), advMng, obj);
}

// при взятии артефакта, пандоры или свитка
_byte_ __stdcall Y_HeroGetObjWithGuard(HiHook* hook, _AdvMgr_* advMng, _Hero_* hero, _MapItem_* obj, _int_ xyz, _byte_ isNotAI)
{
   int xyz_54 = isGuardNearTheObject(advMng, xyz); // проверка клеток по кругу рядом с артефактом
   if ( xyz_54 ) {
      _MapItem_* obj_54 = advMng->map->GetItem(b_unpack_x(xyz_54), b_unpack_y(xyz_54), b_unpack_z(xyz_54));
      advMng->monBattle_type = (_word_)obj_54->os_type; // (advMng+<536>)
      advMng->monBattle_num = (_word_)obj_54->draw_num; // (advMng+<540>)
      advMng->monBattle_side = hero->x < (_word_)((_word_)(xyz_54 << 6) >> 6) ? 1 : 0; // поворот монстра при атаке (advMng+<544>)
      CALL_5(void, __thiscall, 0x4A73B0, advMng, obj_54, hero, xyz_54, isNotAI); // нападаем на монстра рядом с артом
      advMng->monBattle_type = -1; // после битвы возвращаем параметры
      advMng->monBattle_num = -1;  // после битвы возвращаем параметры
      if (hero->owner_id < 0) { // если герой убит или сбежал (т.е. номер хозяина героя стал == -1)
         return 0; // выйти из функции минуя функцию посещения артефакта
      }
   }
   // всё норм (монстры повержены или присоединились) - подбираем артефакт
   return CALL_5(_byte_, __thiscall, hook->GetDefaultFunc(), advMng, hero, obj, xyz, isNotAI);
}

// --------------------------------------------------------------------------------------
_PI->WriteHiHook(0x40E97F, CALL_, EXTENDED_, THISCALL_, Y_GetCursorFrameOnAdvMap);
_PI->WriteHiHook(0x4A82DE, CALL_, EXTENDED_, THISCALL_, Y_HeroGetObjWithGuard);
_PI->WriteHiHook(0x4A8305, CALL_, EXTENDED_, THISCALL_, Y_HeroGetObjWithGuard);
_PI->WriteHiHook(0x4A9D1C, CALL_, EXTENDED_, THISCALL_, Y_HeroGetObjWithGuard);
// --------------------------------------------------------------------------------------


upd (19.11.2018): обновленно из-за найденной ошибки (опечатки)
upd (21.11.2018): обновленно из-за серьезного недочета (была возможна передача отрицательных координат: например когда артефакт находился на границе карты (y=0) велся поиск на y=-1 что приводило к вылету)

Скачать
https://dl.dropboxusercontent.com/s/95j ... tGuard.dll


Cool, but very intensive!
Also misses some cases like Monster in water.
Suggestions:
Код: Выделить всё
#include "patcher_x86.hpp"

#define IsGuarded(x,y,z) (0x0100 & CALL_3(WORD, __fastcall, 0x4F8040, x, y, z)) // replaces isGuardNearTheObject

// keep Y_HeroGetObjWithGuard functions

// for every item. Could always make LoHook around here and customize to your item type
_PI->WriteByte(0x40E22C, 0xEB); // correct mouse cursor ~ replaces Y_GetCursorFrameOnAdvMap


It could be done through pathing itself, without functions.
In a few weeks I will release some bug fix I have been working on recently to give an idea how it can be achieved.
Вернуться к началу

offlineas239  
имя: Анатолий
Ветеран
Ветеран
 
Сообщения: 527
Зарегистрирован: 29 дек 2018, 14:17
Пол: Мужчина
Поблагодарили: 38 раз.

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

Сообщение as239 » 07 фев 2019, 11:43

AlexSpl писал(а):

Код для Учёного без учёта характеристик героя: навыков, наличия книги и т.п. Полагаю, именно это было нужно, а не то, что герой реально получит?

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

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

char TxtBuffer[50];

int __stdcall showScholarInfo(LoHook* h, HookContext* c)
{
    // Учёный?
    if ( c->eax == 0x51 ) {
        int ScholarType = (*(int*)c->ebx << 0x1D) >> 0x1D;
        switch ( ScholarType ) {
            case 0:
                sprintf(TxtBuffer, "%s %s", (char*)c->edi, "(stat)");
                break;
            case 1:
                sprintf(TxtBuffer, "%s %s", (char*)c->edi, "(skill)");
                break;
            case 2:
                sprintf(TxtBuffer, "%s %s", (char*)c->edi, "(cast)");
                break;
        }

        c->edi = (int)TxtBuffer;
    }

    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.Scholar");

            _PI->WriteLoHook(0x40D059, showScholarInfo);
        }
    }

    return TRUE;
}


UPD: Добавил пропущенную проверку на ID объекта.


Да все работает как и задумано, только вот часто бывают такие случаи, когда в описании написано "cast" а герой получает "stat"
Изображение
С чем это связано, думал что с наличием книги у героя, но и с купленной книгой получает "stat"

ИЗМ:
Разобрался, такая ситуация возникает если у героя не хватает мудрости выучить заклинание, так что все работает верно.
Последний раз редактировалось as239 07 фев 2019, 12:08, всего редактировалось 2 раз(а).
Вернуться к началу

offlineas239  
имя: Анатолий
Ветеран
Ветеран
 
Сообщения: 527
Зарегистрирован: 29 дек 2018, 14:17
Пол: Мужчина
Поблагодарили: 38 раз.

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

Сообщение as239 » 07 фев 2019, 11:44

Цитата:
Обновил код для отображения кол-ва потерь (добавил пропущенный хук для дистанционной атаки, учёл пожелание насчёт отображения диапазона):

Спасибо, полезная правка.
Вернуться к началу

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

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

Сообщение AlexSpl » 07 фев 2019, 12:07

Цитата:
С чем это связано, думал что с наличием книги у героя, но и с купленной книгой получает "stat"

Заклинание герой получает, если у него есть книга, нужный уровень Мудрости и герой ещё не знает такого заклинания, в противном случае герой получает +1 к первичному навыку.
Вернуться к началу

offlineas239  
имя: Анатолий
Ветеран
Ветеран
 
Сообщения: 527
Зарегистрирован: 29 дек 2018, 14:17
Пол: Мужчина
Поблагодарили: 38 раз.

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

Сообщение as239 » 07 фев 2019, 12:34

Ben80 писал(а):

as239 писал(а):

Я тоже хотел оттуда вытащить, но не нашел исходники.


Исходники программ такого уровня, как SoD_SP, на дороге, как правило, не валяются.
Далеко не все выкладывают в свободный доступ код актуальных модов.


Написал RoseKavalier просьбу, дать код функции, возвращающей порядок хода.
Вернуться к началу

offlineas239  
имя: Анатолий
Ветеран
Ветеран
 
Сообщения: 527
Зарегистрирован: 29 дек 2018, 14:17
Пол: Мужчина
Поблагодарили: 38 раз.

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

Сообщение as239 » 07 фев 2019, 12:41

AlexSpl писал(а):

Цитата:
С чем это связано, думал что с наличием книги у героя, но и с купленной книгой получает "stat"

Заклинание герой получает, если у него есть книга, нужный уровень Мудрости и герой ещё не знает такого заклинания, в противном случае герой получает +1 к первичному навыку.


Да так и есть, спасибо.
Сейчас ученого практически никто не берет т.к. есть большой шанс поймать совершенно ненужный навык.
А с этой правкой, можно спокойно брать ученых на заклинания и статы.
Причем в заклинании может попасться и портал и дверь измерений, представляю сколько эмоции это может вызвать в сетевой игре :)
Вернуться к началу

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

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

Сообщение AlexSpl » 07 фев 2019, 16:17

Цитата:
Написал RoseKavalier просьбу, дать код функции, возвращающей порядок хода.

Я уважаю чужой труд и не удивлюсь, если он откажет. Всё-таки это часть его мода. Я не утверждаю, что сделать красивую очередь ходов - это проблема, ничего сложного там нет. При желании можно было бы сделать даже разделение на раунды. Я думаю, baratorch давно бы добавил очередь, но самая трудоёмкая часть - корректно обновлять эту "ленту": можно мгновенно, а можно по типу бегущей строки (плавный скроллинг), но не уверен, что движок Героев такое позволяет, т.е. придётся писать свой кастомный код. Если без анимации, то можно было бы попытаться, но как-то не хочется, когда уже есть готовое решение в SoD_SP. И, естественно, это время: одно дело вывести порядок ходов в статусную строку в текстовом виде по нажатию на горячую клавишу, другое - нарисовать окошко в оригинальном стиле с картинками юнитов и обеспечить его корректное обновление. Но, безусловно, при грамотной реализации очередь ходов была бы желанной фичей в HD моде.
Вернуться к началу

Пред.След.

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

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

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

cron