Объявления

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

Нашёл, где был баг. Для игроков-компов неправильно находился следующий игрок.

Код: Выделить всё
#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 getNextActivePlayerID()
{
    int PlayerIDByOrder[9];
   
    int n = 0;
    for (int i = 0; i < 8; ++i)
    {
        if ( o_GameMgr->IsPlayerIngame(i) && o_GameMgr->GetPlayer(i)->IsHuman() ) PlayerIDByOrder[n++] = i;
    }

    for (int i = 0; i < 8; ++i)
    {
        if ( o_GameMgr->IsPlayerIngame(i) && !o_GameMgr->GetPlayer(i)->IsHuman() ) PlayerIDByOrder[n++] = i;
    }
   
    PlayerIDByOrder[n] = PlayerIDByOrder[0];

    int nextActivePlayerID;
    for (int i = 0; i < 8; ++i)
    {
        if ( o_ActivePlayerID == PlayerIDByOrder[i] )
        {
            nextActivePlayerID = PlayerIDByOrder[i + 1];
            break;
        }
    }

    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;
}


newDisguiseBeta.zip
(7.55 КБ) Скачиваний: 277
Последний раз редактировалось AlexSpl 10 окт 2017, 02:12, всего редактировалось 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 » 09 окт 2017, 21:33

Потестировал Ваш плагин. Тест проходит, но есть одно но: игрок видит Disguise на своих героях 3 хода (У меня, как и для всех остальных, 2). Соответственно, в сингле Disguise висит три дня.

Что каcается сложности. Я поищу, может, в геройском коде есть отдельная функция getNextActivePlayerID(). Тогда вместо всех этих функций будет один CALL.

* * *
Блин, Ваш код побил мой. У меня не проходит тест с розовым, а у Вас проходит :smile11:

Исправил. Упростил код.

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

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

Я так понимаю, из хука хотите получить? Для функций со стандартным прологом:
push ebp
mov ebp, esp

Можно получить адрес возврата так: *(int*)(c->ebp + 4). Тогда инструкция call выше и будет инструкцией, вызвавшей нашу функцию. Есть несколько опкодов для call. Чаще всего встречается 5-байтовый E8 xx xx xx xx. Тогда, чтобы достать из этой инструкции адрес вызвавшей функции, нужно отнять от *(int*)(c->ebp + 4) 4 и прочитать относительный адрес: *(int*)(*(int*)(c->ebp + 4) - 4). Теперь адрес фунцкии будет равен

*(int*)(c->ebp + 4) + *(int*)(*(int*)(c->ebp + 4) - 4)

Это сработает для большинства геройских вызовов. Но не для тех вызываемых функций, у которых отсутствует или другой пролог, или когда вызывающая инструкция не call с опкодом E8. Можно, например, функцию вызывать и так: call eax, предварительно поместив адрес в регистр eax.
Вернуться к началу

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

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

Сообщение Ben80 » 10 окт 2017, 06:48

Если хотите, публикуйте версию 1.0 на каком-либо сайте.
Код для героя в замке я добавлять не буду (в оригинальной игре этого нет).
Вообще спелл получается достаточно крутым, надеюсь, что не слишком. В конце концов, на 2 уровне Гильдии и Ослепление, и Молния - тоже очень хорошие спеллы.

Подумаю, как соблюсти баланс для мода в целом. Наверно, экспертное Видение будет позволять видеть истинное положение вещей. При этом Видение предполагаю отнести к Магии воды (саму практику переноса спеллов между школами осуждаю, поскольку это как бы нарушает сложившиеся ассоциативные связи и привычный образ любимой игры). Но в случае Видения это не так критично, поскольку оно не относится к какой-либо школе.

Таким образом, каждая Школа магии должна иметь свои козыри. Только Школа огня, получается, будет лишена хороших походных спеллов, но это компенсируется ее силой в боях. Была идея сделать спелл "Построить портал на карте", в Школе огня, но твердо решил не использовать новых элементов в игре, использовать только то, что от NWC.

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

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

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

Сообщение Ben80 » 10 окт 2017, 06:58

Вода ведь обычно помогает защититься. В данном случае она могла бы защитить от надувательства :smile1:
Вернуться к началу

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

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

Сообщение Ben80 » 10 окт 2017, 13:49

AlexSpl писал(а):

Потестировал Ваш плагин. Тест проходит, но есть одно но: игрок видит Disguise на своих героях 3 хода (У меня, как и для всех остальных, 2). Соответственно, в сингле Disguise висит три дня.


В мультиплеере на своих героях Disguise виден тоже 3 дня - так и должно быть. Это означает, что и в сингле противники видят наш Disguise 2 хода.
Вернуться к началу

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 » 10 окт 2017, 17:22

Цитата:
Если хотите, публикуйте версию 1.0 на каком-либо сайте.

Потестирую пока немного. Ещё хочу заменить четыре хука на один и разобраться с struct _RTL_CRITICAL_SECTION*, если декомпилятор прав насчёт возвращаемого типа. Тогда не будет необходимости ещё в одном хуке, сохраняющем регистры.

Цитата:
В мультиплеере на своих героях Disguise виден тоже 3 дня - так и должно быть. Это означает, что и в сингле противники видят наш Disguise 2 хода.

У меня все всегда видят Disguise 2 хода.

Например, работа алгоритма для 3-х игроков, когда 3-й игрок кастует Disguise:

Ход 3-го. 3-й кастует Disguise, длительность 2 хода, видит 1-й ход.
Ход 1-го. 1-й игрок видит 1-й ход.
Ход 2-го. 2-й видит 1-й ход, уменьшает длительность Disguise для 3-го
Ход 3-го. Длительность 1 ход. Видит 2-й ход.
Ход 1-го. Видит 2-й ход.
Ход 2-го. Видит 2-й ход, уменьшает длительность Disguise для 3-го.
Ход 3-го. Действие заклинания заканчивается.

Получается кольцо.

* * *
В целом последние два плагина неплохими получились. Практически бесполезные навык и заклинание обрели второе дыхание :) Про второй ещё не видел отзывов, но первый вроде как понравился.
Вернуться к началу

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

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

Сообщение Ben80 » 10 окт 2017, 18:32

AlexSpl писал(а):


У меня все всегда видят Disguise 2 хода.

Например, работа алгоритма для 3-х игроков, когда 3-й игрок кастует Disguise:

Ход 3-го. 3-й кастует Disguise, длительность 2 хода, видит 1-й ход.
Ход 1-го. 1-й игрок видит 1-й ход.
Ход 2-го. 2-й видит 1-й ход, уменьшает длительность Disguise для 3-го
Ход 3-го. Длительность 1 ход. Видит 2-й ход.
Ход 1-го. Видит 2-й ход.
Ход 2-го. Видит 2-й ход, уменьшает длительность Disguise для 3-го.
Ход 3-го. Действие заклинания заканчивается.

Получается кольцо.


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

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

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

Сообщение Ben80 » 10 окт 2017, 20:43

AlexSpl писал(а):

Потестирую пока немного. Ещё хочу заменить четыре хука на один


Там и должен быть только один хук - на 41664C.
Все остальные - это когда мы сами просматриваем героя, причем в тех случаях, в которых в оригинальной игре дается честная численность (для этого пушится аргумент равный 2).
В оригинальной игре мы видим своего героя с поддельными войсками только в одном случае - когда он на карте и мы его кликаем правой кнопкой. Это процедура 004164F0 (Dlg_HeroRMCHint - в вогобазе).

Так что остальные 3 хука можно смело удалять (и нужно).
Последний раз редактировалось Ben80 10 окт 2017, 20:48, всего редактировалось 1 раз.
Вернуться к началу

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

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

Сообщение Ben80 » 10 окт 2017, 20:45

Подскажите, пожалуйста, как получить число из инструкции 416632 ?
То, которое в [ebp+var_10]

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

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 » 10 окт 2017, 21:03

Заменил 4 хука makeGhostHero на 1. Всё-таки надо будет разобраться, что такое HiHook :smile14:

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

Patcher* _P;
PatcherInstance* _PI;

static _bool_ plugin_On = 0;

_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 = *(_Hero_**)(c->esp + 0xC);
    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;
            }
        }
     
        *(_Hero_**)(c->esp + 0xC) = ghostHero;
    }

    return EXEC_DEFAULT;
}

int getNextActivePlayerID()
{
    int PlayerIDByOrder[9];
   
    int n = 0;
    for (int i = 0; i < 8; ++i)
    {
        if ( o_GameMgr->IsPlayerIngame(i) && o_GameMgr->GetPlayer(i)->IsHuman() ) PlayerIDByOrder[n++] = i;
    }

    for (int i = 0; i < 8; ++i)
    {
        if ( o_GameMgr->IsPlayerIngame(i) && !o_GameMgr->GetPlayer(i)->IsHuman() ) PlayerIDByOrder[n++] = i;
    }
   
    PlayerIDByOrder[n] = PlayerIDByOrder[0];

    int nextActivePlayerID;
    for (int i = 0; i < 8; ++i)
    {
        if ( o_ActivePlayerID == PlayerIDByOrder[i] )
        {
            nextActivePlayerID = PlayerIDByOrder[i + 1];
            break;
        }
    }

    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 хода, а для последнего игрока-человека - 3, если после него ходят компы)
    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);
            _PI->WriteLoHook(0x52EFB5, makeGhostHero);
        }
    }

    return TRUE;
}


Цитата:
Подскажите, пожалуйста, как получить число из инструкции 416632 ?
То, которое в [ebp+var_10]
Все-таки я пока слабо разбираюсь в указателях.

Просто щёлкаете на var_10 и нажимаете k:

Код: Выделить всё
cmp dword ptr [ebp-10h], 2

Читаем dword (double word/двойное слово) ptr (по адресу) ebp - 10h. dword ptr - это pointer to double word, но я не очень люблю слово "указатель". На С++ будет так: *(int*)(c->ebp - 0x10).

Если eax - это яблоко (с->eax), то [eax] - это яблоко в корзине, т.е. адрес.
Вернуться к началу

Пред.След.

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

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

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