Объявления

Друзья, если не получается зарегистрироваться, напишите на почту 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 » 08 окт 2017, 19:59

Короче, мы на правильном пути: остаток от деления (mod) - это то, что нужно для решения подобных задач. Это как замкнутый круг, а не прямая, как в случае оригинальной механики Disguise. Продолжу завтра :smile11:
Вернуться к началу

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

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

Сообщение Ben80 » 08 окт 2017, 20:44

Хорошо.

У меня тоже есть некий итог :smile1:
Такой вот чудесный код:
Код: Выделить всё
bool Prepared = false;
...
int __stdcall disguiseTownGuards(LoHook* h, HookContext* c)
{
    _Hero_* hero = o_GameMgr->GetHero(c->edx);
    *ghostHero = *hero;
    disguise = (disguiseStruct*)&hero->disguise;
   
    if ( hero->disguise != -1 && Prepared)
    {
        // Копируем героя в "призрачного" героя
        *ghostHero = *hero;
        ghostHero->disguise = -1;

       
        for (int i = 0; i < 7; ++i)
        {
            if ( hero->army.type[i] > ID_NONE )
            {
                ghostHero->army.count[i] = (int)(getDisguiseMultiplier(disguise->index) * ghostHero->army.count[i]);
                // Спасаем единички
                if ( ghostHero->army.count[i] < 1 ) ghostHero->army.count[i] = 1;
            }
        }

    c->eax = (int)(&ghostHero->army);
    }
    Prepared = false;
    return EXEC_DEFAULT;
}

int __stdcall prepareDisguiseTownGuards(LoHook* h, HookContext* c)
{
    Prepared = true;
    return EXEC_DEFAULT;
}

...

_PI->WriteLoHook(0x5C1897, disguiseTownGuards);
_PI->WriteLoHook(0x530C11, prepareDisguiseTownGuards);
Вернуться к началу

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

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

Сообщение Ben80 » 09 окт 2017, 04:06

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

FirstDec должен быть, конечно, массивом на N героев.

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

Patcher* _P;
PatcherInstance* _PI;

static _bool_ plugin_On = 0;

const int ghostHeroHookAddr[] = {0x41664C, 0x4DE583, 0x5D44A0, 0x5D451D};
_Hero_* ghostHero = new _Hero_();

// Выбранный селектор в диалоге (унифицированное значение, не равное выбранному на ползунке)
int selNumber;
bool cancelSpell = false;
bool FirstDec = true;

// Создаём стандартный задник с рамкой цвета игрока
void setStdBackground(_Dlg_* dlg)
{
    CALL_5 (int, __thiscall, 0x48FA80, dlg, dlg->x, dlg->y, dlg->width, dlg->height);

    for (int i = dlg->field_4C; i <= dlg->field_50; ++i)
    {
        CALL_5 (int, __thiscall, 0x5FF400, dlg, 512, 13, i, o_GameMgr->GetMeID());   
    }
}

void __fastcall scrollDlgCallback_1(int click_id, _Dlg_* dlg)
{
    // Выводим в текстовое поле текущее значение, выбранное с помощью ползунка
    // click_id;
    // Уровень Магии воздуха определяет доступные для выбора значения

    if(click_id == 0)
        sprintf(o_TextBuffer, "%s", "Поменьше");
    if(click_id == 1)
        sprintf(o_TextBuffer, "%s", "Побольше");
    // селектор - преобразуем выбранный номер к унифицированной последовательности
    selNumber = click_id + 4;

    ((_DlgStaticText_*)dlg->GetItem(14))->SetText(o_TextBuffer);

    _DlgMsg_ m = {512, 3, 40, 0, 0, 0, 0, 0};
    CALL_2 (void, __thiscall, 0x5FF3A0, dlg, &m);   
    dlg->Redraw(TRUE);
}

void __fastcall scrollDlgCallback_2(int click_id, _Dlg_* dlg)
{
    // Выводим в текстовое поле текущее значение, выбранное с помощью ползунка
    // click_id;
    // Уровень Магии воздуха определяет доступные для выбора значения

    if(click_id == 0)
        sprintf(o_TextBuffer, "%s", "В 2 раза\nменьше");
    if(click_id == 1)
        sprintf(o_TextBuffer, "%s", "Поменьше");
    if(click_id == 2)
        sprintf(o_TextBuffer, "%s", "Побольше");
    if(click_id == 3)
        sprintf(o_TextBuffer, "%s", "В 2 раза\nбольше");
    // селектор - преобразуем выбранный номер к унифицированной последовательности
    selNumber = click_id + 3;

    ((_DlgStaticText_*)dlg->GetItem(14))->SetText(o_TextBuffer);

    _DlgMsg_ m = {512, 3, 40, 0, 0, 0, 0, 0};
    CALL_2 (void, __thiscall, 0x5FF3A0, dlg, &m);   
    dlg->Redraw(TRUE);
}

void __fastcall scrollDlgCallback_3(int click_id, _Dlg_* dlg)
{
    // Выводим в текстовое поле текущее значение, выбранное с помощью ползунка
    // click_id;
    // Уровень Магии воздуха определяет доступные для выбора значения

    if(click_id == 0)
        sprintf(o_TextBuffer, "%s", "В 5 раз\nменьше");
    if(click_id == 1)
        sprintf(o_TextBuffer, "%s", "В 3 раза\nменьше");
    if(click_id == 2)
        sprintf(o_TextBuffer, "%s", "В 2 раза\nменьше");
    if(click_id == 3)
        sprintf(o_TextBuffer, "%s", "Поменьше");
    if(click_id == 4)
        sprintf(o_TextBuffer, "%s", "Побольше");
    if(click_id == 5)
        sprintf(o_TextBuffer, "%s", "В 2 раза\nбольше");
    if(click_id == 6)
        sprintf(o_TextBuffer, "%s", "В 3 раза\nбольше");
    if(click_id == 7)
        sprintf(o_TextBuffer, "%s", "В 5 раз\nбольше");
    // селектор - преобразуем выбранный номер к унифицированной последовательности
    selNumber = click_id + 1;

    ((_DlgStaticText_*)dlg->GetItem(14))->SetText(o_TextBuffer);

    _DlgMsg_ m = {512, 3, 40, 0, 0, 0, 0, 0};
    CALL_2 (void, __thiscall, 0x5FF3A0, dlg, &m);   
    dlg->Redraw(TRUE);
}


void yDlgShow(int width, int height, int count, char* captionText, char* messageText, bool middle = true, int x = 0, int y = 0)
{
    // Создаём по центру экрана пустой диалог с тенью
    _Dlg_* dlg = _CustomDlg_::Create(DLG_X_CENTER, DLG_Y_CENTER, width, height, DF_SCREENSHOT | DF_SHADOW, NULL);
   
    // Добавляем задник (фон) и рамку по цвету игрока
    setStdBackground(dlg);
       
    // Добавляем заголовок диалога
    dlg->AddItem(_DlgStaticText_::Create(0, 20, dlg->width, 30,
        captionText, "bigfont.fnt", 7, 1, ALIGN_H_CENTER | ALIGN_V_TOP, 0));

    // Добавляем текст
    dlg->AddItem(_DlgStaticText_::Create(0, 50, dlg->width, 50,
        messageText, "medfont.fnt", 1, 0, ALIGN_H_CENTER | ALIGN_V_TOP, 0));

    // Добавляем полосу горизонтальной прокрутки
    //dlg->AddItem(_DlgScroll_::Create(30, dlg->height - 90, dlg->width - 30 * 2 + 1, 16, 40, count,
    //    (_ptr_)scrollDlgCallback, 0, 18, 0));
   
   
    // Добавляем текстовое поле для отображения значений, выбираемых с помощью ползунка
    if(count == 2)
    {
        dlg->AddItem(_DlgStaticText_::Create((dlg->width - 72) / 2 + 1, dlg->height - 65, 72, 40,
            "Поменьше", "medfont.fnt", 1, 14, ALIGN_H_CENTER | ALIGN_V_CENTER, 0));
        dlg->AddItem(_DlgScroll_::Create(30, dlg->height - 90, dlg->width - 30 * 2 + 1, 16, 40, count,
        (_ptr_)scrollDlgCallback_1, 0, 0, 0));
        selNumber = 4;
    }
    if(count == 4)
    {
        dlg->AddItem(_DlgStaticText_::Create((dlg->width - 72) / 2 + 1, dlg->height - 65, 72, 40,
            "В 2 раза\nменьше", "medfont.fnt", 1, 14, ALIGN_H_CENTER | ALIGN_V_CENTER, 0));
        dlg->AddItem(_DlgScroll_::Create(30, dlg->height - 90, dlg->width - 30 * 2 + 1, 16, 40, count,
        (_ptr_)scrollDlgCallback_2, 0, 0, 0));
        selNumber = 3;
    }
    if(count == 8)
    {
        dlg->AddItem(_DlgStaticText_::Create((dlg->width - 72) / 2 + 1, dlg->height - 65, 72, 40,
            "В 5 раз\nменьше", "medfont.fnt", 1, 14, ALIGN_H_CENTER | ALIGN_V_CENTER, 0));
        dlg->AddItem(_DlgScroll_::Create(30, dlg->height - 90, dlg->width - 30 * 2 + 1, 16, 40, count,
        (_ptr_)scrollDlgCallback_3, 0, 0, 0));
        selNumber = 1;
    }

    // Добавляем кнопку OK
    dlg->AddItem(_DlgStaticPcx8_::Create(30, dlg->height - 61, 0, "Box64x30.pcx"));
    dlg->AddItem(_DlgButton_::Create(31, dlg->height - 60, 64, 30, 30722, "iOkay.def", 0, 1, 1, 28, 2));

    // Добавляем кнопку Cancel
    dlg->AddItem(_DlgStaticPcx8_::Create(dlg->width - 31 - 64, dlg->height - 61, 0, "Box64x30.pcx"));
    dlg->AddItem(_DlgButton_::Create(dlg->width - 30 - 64, dlg->height - 60, 64, 30, 30721, "iCancel.def", 0, 1, 1, 1, 2));

    if ( !middle ) dlg->SetPos(x, y);
    dlg->Run();
    dlg->Destroy(TRUE);
}

float __stdcall getUnpackedMultiplier(int n)
{
    float selFloat[8] = {0.2, 0.333, 0.5, 0.666, 1.5, 2.0, 3.0, 5.0};
    return selFloat[n/2 - 1];
}

int __stdcall makeGhostHero(LoHook* h, HookContext* c)
{
    _Hero_* hero;
    int* heroReg;
    float multiplier = 1.0;

    h->GetAddress() == ghostHeroHookAddr[0] ? heroReg = &c->esi : heroReg = &c->eax;
    hero = *(_Hero_**)heroReg;
   
    if ( hero->disguise != -1)
    {
        // Копируем героя в "призрачного" героя
        *ghostHero = *hero;
       
        // Своих героев просматриваем в истинном виде (в оригинальной игре это не так)
        //if(hero->owner_id != o_GameMgr->GetMeID())
        multiplier = getUnpackedMultiplier((int)hero->disguise);

        ghostHero->disguise = -1;
     
        for (int i = 0; i < 7; ++i)
        {
            ghostHero->army.count[i] = ghostHero->army.count[i] * multiplier;
        }
     
        *(int*)heroReg = (int)ghostHero;
    }

    return EXEC_DEFAULT;
}


int __stdcall decPackedDisguise(LoHook* h, HookContext* c)
{
    for (int i = 0; i < o_HEROES_COUNT; ++i)
    {
        if ( o_GameMgr->GetHero(i)->owner_id == o_GameMgr->GetMeID() && o_GameMgr->GetHero(i)->disguise != -1 && o_ActivePlayer->IsHuman())
        {
            if(((int)(o_GameMgr->GetHero(i)->disguise)) % 2 == 0)
                {
                    o_GameMgr->GetHero(i)->disguise = -1;
                    FirstDec = true;
                 }
            else
                if(FirstDec == false || o_NetworkGame == false)
                {
                    --o_GameMgr->GetHero(i)->disguise;
                }
                else
                    FirstDec = false;
        }
    }

    return EXEC_DEFAULT;
}

int __stdcall setPackedDisguise(LoHook* h, HookContext* c)
{
    // Получаем количество делений для ползунка исходя из уровня Магии воздуха
    int local_ecx = c->ecx;
    int local_eax = c->eax;
    cancelSpell = false;
    int arPos[4] = {2,2,4,8};
    int nPos = arPos[*(char*)(c->esi + 201 + 15)];
    // Если герой скастовал Disguise, показываем диалог
    //yDlgShow(240, 140, nPos, "Маскировка", "Выберите мнимую\nсилу отрядов");
    yDlgShow(300, 175, nPos, "Маскировка", "Выберите мнимую\nсилу отрядов");
   
    // Если нажата кнопка OK (30722), то формируем поле disguise у героя,
    // проигрываем WAV файл
    // иначе - возвращаем герою израсходованную ману
    if ( o_WndMgr->result_dlg_item_id == 30722 )
    {
        c->edi = selNumber * 2 + 1;
        //c->eax = 0x6854A0;
        //c->ecx = *(int*)(0x6856C4);
        //CALL_0(__int64, __fastcall, 0x59A770);
        //c->ecx = local_ecx;
        //c->eax = local_eax;
    }
    else
    {
        c->edi = -1;
        cancelSpell = true;
    }
   

    return EXEC_DEFAULT;
}

int __stdcall cancelSpellCost(LoHook* h, HookContext* c)
{
    if(cancelSpell == true)
        c->eax = 0;

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

         _PI->WriteHexPatch(0x4C7DA5, "90 90 90");
         //_PI->WriteHexPatch(0x41C7BA, "90 90 90 90 90");
         _PI->WriteLoHook(0x4C700B, decPackedDisguise);
         _PI->WriteLoHook(0x41C7C6, setPackedDisguise);
         _PI->WriteLoHook(0x41C7DD, cancelSpellCost);
         for (int i = 0; i < 4; ++i) _PI->WriteLoHook(ghostHeroHookAddr[i], makeGhostHero);
        }
    }

   return TRUE;
}
Последний раз редактировалось Ben80 09 окт 2017, 04:37, всего редактировалось 2 раз(а).
Вернуться к началу

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

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

Сообщение Ben80 » 09 окт 2017, 04:08

Основное в

Код: Выделить всё
int __stdcall decPackedDisguise(LoHook* h, HookContext* c)
{
    for (int i = 0; i < o_HEROES_COUNT; ++i)
    {
        if ( o_GameMgr->GetHero(i)->owner_id == o_GameMgr->GetMeID() && o_GameMgr->GetHero(i)->disguise != -1 && o_ActivePlayer->IsHuman())
        {
            if(((int)(o_GameMgr->GetHero(i)->disguise)) % 2 == 0)
                {
                    o_GameMgr->GetHero(i)->disguise = -1;
                    FirstDec = true;
                 }
            else
                if(FirstDec == false || o_NetworkGame == false)
                {
                    --o_GameMgr->GetHero(i)->disguise;
                }
                else
                    FirstDec = false;
        }
    }

    return EXEC_DEFAULT;
}

...

_PI->WriteLoHook(0x4C700B, decPackedDisguise);
Вернуться к началу

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

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

Сообщение Ben80 » 09 окт 2017, 15:58

Объединил Ваш код с моим, вот что получилось. Все работает, по крайней мере на первый взгляд.
В код не добавил только Disguise для героя в замке.
NewDisguiseRus.zip
(5.87 КБ) Скачиваний: 185


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

Patcher* _P;
PatcherInstance* _PI;

static _bool_ plugin_On = 0;

const int ghostHeroHookAddr[] = {0x41664C, 0x4DE583, 0x5D44A0, 0x5D451D};
_Hero_* ghostHero = new _Hero_();
_Hero_* curHero;

int nPos, s_ebx, s_edx;

struct disguiseStruct
{
    char duration : 4;
    char index : 4;
    char unusedByte;
    short unusedWord;
} *disguise;

float getDisguiseMultiplier(int n)
{
   float multiplier[] = {1.0f, 1.5f, 2.0f, 3.0f, 5.0f};
   return n >= 0 ? multiplier[n] : 1.0f / multiplier[-n - 1];
}

void getArmyCountText(const _Hero_* hero, const int stackId, const int index = 0)
{
    sprintf(o_TextBuffer, "%s", "");
   
    if ( hero->army.type[stackId] > ID_NONE )
    {
        int armyCount = (int)(getDisguiseMultiplier(index) * hero->army.count[stackId]);
         
        // Спасаем единички
        if ( armyCount < 1 ) armyCount = 1;
        if ( armyCount < 10000 ) sprintf(o_TextBuffer, "%d", armyCount);
            else sprintf(o_TextBuffer, "%dт", armyCount / 1000);
    }
}


// Создаём стандартный задник с рамкой цвета игрока
void setStdBackground(_Dlg_* dlg)
{
    CALL_5 (int, __thiscall, 0x48FA80, dlg, dlg->x, dlg->y, dlg->width, dlg->height);

    for (int i = dlg->field_4C; i <= dlg->field_50; ++i)
    {
        CALL_5 (int, __thiscall, 0x5FF400, dlg, 512, 13, i, o_GameMgr->GetMeID());   
    }
}


void __fastcall scrollDlgCallback(int click_id, _Dlg_* dlg)
{
    nPos = click_id - ((_DlgScroll_*)dlg->GetItem(40))->ticks_count / 2;
    dlg->GetItem(30722)->SetEnabled(nPos != 0);
    if ( nPos < 0 ) --nPos;
    float multiplier = getDisguiseMultiplier(nPos);
   
    char Text[10];
    sprintf(Text, "%d%s", (int)(multiplier * 100), "%");

   ((_DlgStaticText_*)dlg->GetItem(14))->SetText(Text);

    for (int i = 0; i < 7; ++i)
    {
        getArmyCountText(curHero, i, nPos);
        ((_DlgStaticText_*)dlg->GetItem(20 + i))->SetText(o_TextBuffer);
    }

    // Устанавливаем ползунок в ближайшее к точке клика положение
    _DlgMsg_ m = {512, 3, 40, 0, 0, 0, 0, 0};
    CALL_2(void, __thiscall, 0x5FF3A0, dlg, &m);
    dlg->Redraw(TRUE);
}

void yDlgShow(int width, int height, int count, char* captionText, char* messageText, bool middle = true, int x = 0, int y = 0)
{
    // Создаём по центру экрана пустой диалог с тенью
    _Dlg_* dlg = _CustomDlg_::Create(DLG_X_CENTER, DLG_Y_CENTER, width, height, DF_SCREENSHOT | DF_SHADOW, NULL);
   
    // Добавляем задник (фон) и рамку по цвету игрока
    setStdBackground(dlg);

    // Добавляем заголовок диалога
    dlg->AddItem(_DlgStaticText_::Create(0, 24, dlg->width, 30,
        captionText, "bigfont.fnt", 7, 1, ALIGN_H_CENTER | ALIGN_V_TOP, 0));

    // Добавляем текст
    dlg->AddItem(_DlgStaticText_::Create(0, 54, dlg->width, 50,
        messageText, "medfont.fnt", 1, 0, ALIGN_H_CENTER | ALIGN_V_TOP, 0));

    // Добавляем иконки армии и численность
    for (int i = 0; i < 7; ++i)
    {
        int armyTypeId = ID_NONE;
        if ( curHero->army.type[i] > ID_NONE ) armyTypeId = curHero->army.type[i] + 2;

        int leftX = (dlg->width - 236) / 2;
        // Добавляем тёмный фон для иконок
        dlg->AddItem(_DlgStaticDef_::Create(leftX + i * 34, 110, 0,
            "cprsmall.def", 0, 0, 0));
       
        // Добавляем иконки армии
        if ( armyTypeId > ID_NONE )
        {
            dlg->AddItem(_DlgStaticDef_::Create(leftX + i * 34, 110, 30 + i,
                "cprsmall.def", armyTypeId, 0, 0));
        }
       
        // Добавляем рамку для иконок
        dlg->AddItem(_DlgStaticDef_::Create(leftX + i * 34, 110, 0, "cprsmall.def", 1, 0, 0));
       
        // Добавляем надписи с численностью отрядов
        getArmyCountText(curHero, i);
        dlg->AddItem(_DlgStaticText_::Create(leftX + 1 + i * 34, 144, 32, 16,
            o_TextBuffer, "tiny.fnt", 1, 20 + i, ALIGN_H_CENTER | ALIGN_V_TOP, 0));

    }

    // Добавляем полосу горизонтальной прокрутки и устанавливаем ползунок по центру
    _DlgScroll_* disguiseScroll = _DlgScroll_::Create(30, dlg->height - 90, dlg->width - 30 * 2 + 1, 16, 40, count,
        (_ptr_)scrollDlgCallback, 0, 0, 0);
    disguiseScroll->tick = count / 2;
    disguiseScroll->tick_value = count / 2;
    disguiseScroll->btn_position = (disguiseScroll->width - disguiseScroll->btn_size2) / 2;
    dlg->AddItem(disguiseScroll);
   
    // Добавляем текстовое поле для отображения значений, выбираемых с помощью ползунка
    dlg->AddItem(_DlgStaticText_::Create((dlg->width - 72) / 2 + 1, dlg->height - 65, 72, 40,
        "100%", "medfont.fnt", 1, 14, ALIGN_H_CENTER | ALIGN_V_CENTER, 0));

    // Добавляем кнопку OK
    dlg->AddItem(_DlgStaticPcx8_::Create(30, dlg->height - 61, 0, "Box64x30.pcx"));
    dlg->AddItem(_DlgButton_::Create(31, dlg->height - 60, 64, 30, 30722, "iOkay.def", 0, 1, 1, 28, 2));
    dlg->GetItem(30722)->SetEnabled(0);

    // Добавляем кнопку Cancel
    dlg->AddItem(_DlgStaticPcx8_::Create(dlg->width - 31 - 64, dlg->height - 61, 0, "Box64x30.pcx"));
    dlg->AddItem(_DlgButton_::Create(dlg->width - 30 - 64, dlg->height - 60, 64, 30, 30721, "iCancel.def", 0, 1, 1, 1, 2));

    if ( !middle ) dlg->SetPos(x, y);
    dlg->Run();
    dlg->Destroy(TRUE);
}

int __stdcall makeGhostHero(LoHook* h, HookContext* c)
{
    _Hero_* hero;
    int* heroReg;

    h->GetAddress() == ghostHeroHookAddr[0] ? heroReg = &c->esi : heroReg = &c->eax;
    hero = *(_Hero_**)heroReg;
    disguise = (disguiseStruct*)&hero->disguise;

    if ( hero->disguise != -1 )
    //if ( disguise != -1 )
    {
        // Копируем героя в "призрачного" героя
        *ghostHero = *hero;
        ghostHero->disguise = -1;

        for (int i = 0; i < 7; ++i)
        {
            if ( hero->army.type[i] > ID_NONE )
            {
                ghostHero->army.count[i] = (int)(getDisguiseMultiplier(disguise->index) * ghostHero->army.count[i]);

                // Спасаем единички
                if ( ghostHero->army.count[i] < 1 ) ghostHero->army.count[i] = 1;
            }
        }
     
        *(int*)heroReg = (int)ghostHero;
    }

    return EXEC_DEFAULT;
}


int __stdcall decDisguise(LoHook* h, HookContext* c)
{
    for (int i = 0; i < o_HEROES_COUNT; ++i)
    {
        if ( o_ActivePlayer->IsHuman() && o_GameMgr->GetHero(i)->owner_id == o_GameMgr->GetMeID() )
        {
            disguise = (disguiseStruct*)&o_GameMgr->GetHero(i)->disguise;
            if (  disguise->duration != -1 ) --disguise->duration;
            if (disguise->duration == -1) o_GameMgr->GetHero(i)->disguise = -1;
        }
    }

    return EXEC_DEFAULT;
}

int __stdcall setDisguise(LoHook* h, HookContext* c)
{
    nPos = 0;
    _Hero_* hero = (_Hero_*)c->esi;
    curHero = hero;

   // Показываем диалог с горизонтальной полосой прокрутки на 3/5/9 значений
   // для Базового/Продвинутого/Экспертного навыка
    int count[4] = {3,3,5,9};
    char Text[100];
    sprintf(Text, "{%s}, выберите мнимую\nсилу армии", hero->name);
    yDlgShow(320, 256, count[hero->second_skill[HSS_AIR_MAGIC]], "{Маскировка}", Text);

    bool disguiseCast = o_WndMgr->result_dlg_item_id == 30722;

    // Индекс множителя маскировки помещаем в старший полубайт,
    // длительность "Маскировки" (2 хода) - в младший
    if ( disguiseCast )
    {
        disguise = (disguiseStruct*)&hero->disguise;
        disguise->index = nPos;
        if(o_NetworkGame)
            disguise->duration = 2;
        else
            disguise->duration = 1;

        // Расходуем ману
        CALL_2(int, __thiscall, 0x4D9540, hero, c->eax);

        // Проигрываем звук Disguise
        CALL_1(void, __thiscall, 0x59A770, *(int*)(*(int*)0x687FA8 + 548));
       
        // Выполняем функцию, параметры для которой вернула функция воспроизведения WAV
        CALL_3(void, __thiscall, 0x59A7C0, -1, s_ebx, s_edx);
    }
   
    c->return_address = 0x41C7F2;
    return NO_EXEC_DEFAULT;
}


int __stdcall saveRegs(LoHook* h, HookContext* c)
{
    s_ebx = c->ebx;
    s_edx = c->edx;
   
    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.NewDisguise");
         
            _PI->WriteCodePatch(0x41C7BA, "%n", 5);
            _PI->WriteCodePatch(0x4C7DA5, "%n", 3);
            _PI->WriteCodePatch(0x41C7C6, "%n", 6);

            _PI->WriteLoHook(0x4C700B, decDisguise);
            _PI->WriteLoHook(0x41C7CC, saveRegs);
            _PI->WriteLoHook(0x41C7DD, setDisguise);
            for (int i = 0; i < 4; ++i) _PI->WriteLoHook(ghostHeroHookAddr[i], makeGhostHero);
        }
    }

   return TRUE;
}
Последний раз редактировалось Ben80 09 окт 2017, 18:38, всего редактировалось 3 раз(а).
Вернуться к началу

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

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

Сообщение Ben80 » 09 окт 2017, 16:24

Такое вот интересное условие присутствует в коде:

Код: Выделить всё
        if(o_NetworkGame)
            disguise->duration = 2;
        else
            disguise->duration = 1;
Вернуться к началу

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

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

Сообщение Ben80 » 09 окт 2017, 16:27

Подскажите, пожалуйста, как внутри процедуры узнать адрес, на который она возвратится потом ?
Пробовал через c->esp, не получилось.

Внутри процедуры 5C1860 хочется проверять, что ее вызывали из 530C11
Вернуться к началу

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 » 09 окт 2017, 19:26

Вроде добил. Тестируйте.

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

Patcher* _P;
PatcherInstance* _PI;

static _bool_ plugin_On = 0;

const int ghostHeroHookAddr[] = {0x41664C, 0x4DE583, 0x5D44A0, 0x5D451D};
_Hero_* ghostHero = new _Hero_();
_Hero_* curHero;

int nPos, s_ebx, s_edx;

struct disguiseStruct
{
    char duration : 4;
    char index : 4;
    char unusedByte;
    short unusedWord;
} *disguise;

float getDisguiseMultiplier(int n)
{
    float multiplier[] = {1.0f, 1.5f, 2.0f, 3.0f, 5.0f};
    return n >= 0 ? multiplier[n] : 1.0f / multiplier[-n - 1];
}

void getArmyCountText(const _Hero_* hero, const int stackId, const int index = 0)
{
    sprintf(o_TextBuffer, "%s", "");
   
    if ( hero->army.type[stackId] > ID_NONE )
    {
        int armyCount = (int)(getDisguiseMultiplier(index) * hero->army.count[stackId]);
       
        // Не палимся, если выбрали модификатор, отличный от нуля
        if ( armyCount < 1 ) armyCount = 1;
       
        if ( armyCount < 10000 ) sprintf(o_TextBuffer, "%d", armyCount);
            else sprintf(o_TextBuffer, "%dk", armyCount / 1000);
    }
}

// Создаём стандартный задник с рамкой цвета игрока
void setStdBackground(_Dlg_* dlg)
{
    CALL_5(int, __thiscall, 0x48FA80, dlg, dlg->x, dlg->y, dlg->width, dlg->height);

    for (_ptr_ i = dlg->field_4C; i <= dlg->field_50; ++i)
    {
        CALL_5(int, __thiscall, 0x5FF400, dlg, 512, 13, i, o_GameMgr->GetMeID());   
    }
}

void __fastcall scrollDlgCallback(int click_id, _Dlg_* dlg)
{
    nPos = click_id - ((_DlgScroll_*)dlg->GetItem(40))->ticks_count / 2;
    dlg->GetItem(30722)->SetEnabled(nPos != 0);
    if ( nPos < 0 ) --nPos;
    float multiplier = getDisguiseMultiplier(nPos);
   
    sprintf(o_TextBuffer, "%d%s", (int)(multiplier * 100), "%");

    ((_DlgStaticText_*)dlg->GetItem(14))->SetText(o_TextBuffer);

    for (int i = 0; i < 7; ++i)
    {
        getArmyCountText(curHero, i, nPos);
        ((_DlgStaticText_*)dlg->GetItem(20 + i))->SetText(o_TextBuffer);
    }

    // Устанавливаем ползунок в ближайшее к точке клика положение
    _DlgMsg_ m = {512, 3, 40, 0, 0, 0, 0, 0};
    CALL_2(void, __thiscall, 0x5FF3A0, dlg, &m);
    dlg->Redraw(TRUE);
}

void yDlgShow(int width, int height, int count, char* captionText, char* messageText, bool middle = true, int x = 0, int y = 0)
{
    // Создаём по центру экрана пустой диалог с тенью
    _Dlg_* dlg = _CustomDlg_::Create(DLG_X_CENTER, DLG_Y_CENTER, width, height, DF_SCREENSHOT | DF_SHADOW, NULL);
   
    // Добавляем задник (фон) и рамку по цвету игрока
    setStdBackground(dlg);
       
    // Добавляем заголовок диалога
    dlg->AddItem(_DlgStaticText_::Create(0, 24, dlg->width, 30,
        captionText, "bigfont.fnt", 7, 1, ALIGN_H_CENTER | ALIGN_V_TOP, 0));

    // Добавляем текст
    dlg->AddItem(_DlgStaticText_::Create(0, 54, dlg->width, 50,
        messageText, "medfont.fnt", 1, 0, ALIGN_H_CENTER | ALIGN_V_TOP, 0));

    // Добавляем иконки армии и численность
    for (int i = 0; i < 7; ++i)
    {
        int armyTypeId = ID_NONE;
        if ( curHero->army.type[i] > ID_NONE ) armyTypeId = curHero->army.type[i] + 2;

        int leftX = (dlg->width - 236) / 2;
        // Добавляем тёмный фон для иконок
        dlg->AddItem(_DlgStaticDef_::Create(leftX + i * 34, 110, 0,
            "cprsmall.def", 0, 0, 0));
       
        // Добавляем иконки армии
        if ( armyTypeId > ID_NONE )
        {
            dlg->AddItem(_DlgStaticDef_::Create(leftX + i * 34, 110, 30 + i,
                "cprsmall.def", armyTypeId, 0, 0));
        }
       
        // Добавляем рамку для иконок
        dlg->AddItem(_DlgStaticDef_::Create(leftX + i * 34, 110, 0, "cprsmall.def", 1, 0, 0));
       
        // Добавляем надписи с численностью отрядов
        getArmyCountText(curHero, i);
        dlg->AddItem(_DlgStaticText_::Create(leftX + 1 + i * 34, 144, 32, 16,
            o_TextBuffer, "tiny.fnt", 1, 20 + i, ALIGN_H_CENTER | ALIGN_V_TOP, 0));
    }

    // Добавляем полосу горизонтальной прокрутки и устанавливаем ползунок по центру
    _DlgScroll_* disguiseScroll = _DlgScroll_::Create(30, dlg->height - 90, dlg->width - 30 * 2 + 1, 16, 40, count,
        (_ptr_)scrollDlgCallback, 0, 0, 0);
    disguiseScroll->tick = count / 2;
    disguiseScroll->tick_value = count / 2;
    disguiseScroll->btn_position = (disguiseScroll->width - disguiseScroll->btn_size2) / 2;
    dlg->AddItem(disguiseScroll);
   
    // Добавляем текстовое поле для отображения значений, выбираемых с помощью ползунка
    dlg->AddItem(_DlgStaticText_::Create((dlg->width - 72) / 2 + 1, dlg->height - 65, 72, 40,
        "100%", "medfont.fnt", 1, 14, ALIGN_H_CENTER | ALIGN_V_CENTER, 0));

    // Добавляем кнопку OK
    dlg->AddItem(_DlgStaticPcx8_::Create(30, dlg->height - 61, 0, "Box64x30.pcx"));
    dlg->AddItem(_DlgButton_::Create(31, dlg->height - 60, 64, 30, 30722, "iOkay.def", 0, 1, 1, 28, 2));
    dlg->GetItem(30722)->SetEnabled(0);

    // Добавляем кнопку Cancel
    dlg->AddItem(_DlgStaticPcx8_::Create(dlg->width - 31 - 64, dlg->height - 61, 0, "Box64x30.pcx"));
    dlg->AddItem(_DlgButton_::Create(dlg->width - 30 - 64, dlg->height - 60, 64, 30, 30721, "iCancel.def", 0, 1, 1, 1, 2));

    if ( !middle ) dlg->SetPos(x, y);
    dlg->Run();
    dlg->Destroy(TRUE);
}

int __stdcall makeGhostHero(LoHook* h, HookContext* c)
{
    _Hero_* hero;
    int* heroReg;

    h->GetAddress() == ghostHeroHookAddr[0] ? heroReg = &c->esi : heroReg = &c->eax;
    hero = *(_Hero_**)heroReg;
    disguise = (disguiseStruct*)&hero->disguise;

    if ( hero->disguise != -1 )
    {
        // Копируем героя в "призрачного" героя
        *ghostHero = *hero;
        ghostHero->disguise = -1;

        for (int i = 0; i < 7; ++i)
        {
            if ( hero->army.type[i] > ID_NONE )
            {
                ghostHero->army.count[i] = (int)(getDisguiseMultiplier(disguise->index) * ghostHero->army.count[i]);

                // Не палимся, если выбрали модификатор, отличный от нуля
                if ( ghostHero->army.count[i] < 1 ) ghostHero->army.count[i] = 1;
            }
        }
     
        *(int*)heroReg = (int)ghostHero;
    }

    return EXEC_DEFAULT;
}

int getActivePlayersCount()
{
    int activePlayersCount = 0;
    for (int i = 0; i < 8; ++i)
    {
        if ( o_GameMgr->IsPlayerIngame(i) ) ++activePlayersCount;
    }

    return activePlayersCount;
}

int findNextActiveHumanPlayerID(int currentPlayerID)
{
    int nextActivePlayerID = currentPlayerID;

    do {
        ++nextActivePlayerID;
    } while ( nextActivePlayerID < 8 && (o_GameMgr->IsPlayerOutgame(nextActivePlayerID) || !o_GameMgr->GetPlayer(nextActivePlayerID)->IsHuman()) );

    return nextActivePlayerID;
}

int findNextActivePlayerID(int currentPlayerID)
{
    int nextActivePlayerID = currentPlayerID;

    do {
        ++nextActivePlayerID %= 8;
    } while ( o_GameMgr->IsPlayerOutgame(nextActivePlayerID) );

    return nextActivePlayerID;
}

int getNextActivePlayerID()
{
    int nextActivePlayerID = o_ActivePlayerID;

    // Если игрок единственный, то следующим ходит он
    if ( getActivePlayersCount() == 1 ) return nextActivePlayerID;
   
    // Если активный игрок человек, ищем следующего активного игрока
    if ( o_GameMgr->GetPlayer(nextActivePlayerID)->IsHuman() )
    {
        // Ищем следующего активного игрока-человека
        nextActivePlayerID = findNextActiveHumanPlayerID(nextActivePlayerID);
       
        // Если не нашли, ищем следующего активного игрока
        if ( nextActivePlayerID == 8 )
        {
            nextActivePlayerID = findNextActivePlayerID(o_ActivePlayerID);
        }
    }
    // Если активный игрок комп, ищем следующего активного игрока
    else
    {
        // Ищем следующего активного игрока
        nextActivePlayerID = findNextActivePlayerID(nextActivePlayerID);
    }

    return nextActivePlayerID;
}

int __stdcall decDisguise(LoHook* h, HookContext* c)
{
    int nextActivePlayerID = getNextActivePlayerID();

    for (int i = 0; i < o_HEROES_COUNT; ++i)
    {
        if ( o_GameMgr->GetHero(i)->owner_id == nextActivePlayerID )
        {
            disguise = (disguiseStruct*)&o_GameMgr->GetHero(i)->disguise;
            if (  disguise->duration != -1 )
            {
                if ( --disguise->duration == -1 ) o_GameMgr->GetHero(i)->disguise = -1;
            }
        }
    }

    return EXEC_DEFAULT;
}

int __stdcall setDisguise(LoHook* h, HookContext* c)
{
    nPos = 0;
    _Hero_* hero = (_Hero_*)c->esi;
   
    curHero = hero;

    // Показываем диалог с горизонтальной полосой прокрутки
    // на 3/3/5/9 значений для Основного/Базового/Продвинутого/Экспертного навыка
    sprintf(o_TextBuffer, "{%s}, select your army\nvisible strength.", hero->name);
   
    int tickCount[] = {3, 3, 5, 9};
    yDlgShow(320, 256, tickCount[hero->second_skill[HSS_AIR_MAGIC]], "{Disguise}", o_TextBuffer);
    bool disguiseCast = o_WndMgr->result_dlg_item_id == 30722;

    // Индекс множителя маскировки помещаем в старший полубайт,
    // длительность "Маскировки" (2 хода) - в младший
    if ( disguiseCast )
    {
        disguise = (disguiseStruct*)&hero->disguise;
        disguise->index = nPos;
        disguise->duration = 1;

        // Расходуем ману
        CALL_2(int, __thiscall, 0x4D9540, hero, c->eax);

        // Проигрываем звук Disguise
        CALL_1(void, __thiscall, 0x59A770, *(int*)(*(int*)0x687FA8 + 548));
       
        // Выполняем функцию, параметры для которой вернула функция воспроизведения WAV
        CALL_3(void, __thiscall, 0x59A7C0, -1, s_ebx, s_edx);
    }
   
    c->return_address = 0x41C7F2;
    return NO_EXEC_DEFAULT;
}

int __stdcall saveRegs(LoHook* h, HookContext* c)
{
    s_ebx = c->ebx;
    s_edx = c->edx;
   
    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.TestDisguise");

            _PI->WriteCodePatch(0x4C7DA5, "%n", 3);
            _PI->WriteCodePatch(0x41C7C6, "%n", 6);
            _PI->WriteCodePatch(0x41C7BA, "%n", 5);
            _PI->WriteLoHook(0x41C7CC, saveRegs);
            _PI->WriteLoHook(0x41C7DD, setDisguise);
            _PI->WriteLoHook(0x4C6CA9, decDisguise);
            for (int i = 0; i < 4; ++i) _PI->WriteLoHook(ghostHeroHookAddr[i], makeGhostHero);
        }
    }

    return TRUE;
}

Проверил в сингле:
а) Один игрок;
б) Один игрок + компы;

В хотсите:
а) Два игрока;
б) Два игрока + компы;

В сетевой игре:
а) Два игрока.

Работает корректно для красного и для синего. Оба игрока видят Disguise соперника ровно 2 хода.

newDisguise099.zip
(7.6 КБ) Скачиваний: 180
Если кто подскажет функцию, которая возвращает следующего активного игрока, заменю ею собственную getNextActivePlayerID().
Вернуться к началу

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

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

Сообщение Ben80 » 09 окт 2017, 19:47

AlexSpl писал(а):

Вроде добил. Тестируйте.


Потестирую. Только и Вы тоже потестируйте код выше.
Я просто пока не вижу резона применять такой усложненный код, если и так все работает, как задумано.
Последний раз редактировалось Ben80 09 окт 2017, 19:53, всего редактировалось 1 раз.
Вернуться к началу

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

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

Сообщение Ben80 » 09 окт 2017, 19:51

Ben80 писал(а):

Такое вот интересное условие присутствует в коде:

Код: Выделить всё
        if(o_NetworkGame)
            disguise->duration = 2;
        else
            disguise->duration = 1;


Кстати, если хук установить в начале функции 4C6CA0, то это условие не нужно, просто
Код: Выделить всё
disguise->duration = 2;
Вернуться к началу

Пред.След.

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

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

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