Объявления

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

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

Герои Меча и Магии III: Возрождение Эрафии, Герои Меча и Магии III Дыхание Смерти, Герои Меча и Магии III Клинок Армагеддона, Герои Меча и Магии III Хроники Героев
offlineSerp
 
Сообщения: 8
Зарегистрирован: 05 окт 2017, 11:21
Поблагодарили: 13 раз.

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

Сообщение Serp » 05 окт 2017, 14:07

Authors:
igrik, Serp

Titel:
NewArtifactsMerchants

Description:
Fills empty slots of artifact merchants every new week with new artifacts.

Works also in Multiplayer. Every player should have this plugin installed/uninstalled, to not mess up the game.
I'm not sure if it works without HotA, not tested.

 Code
Код: Выделить всё
// code from igrik http://handbookhmm.ru/forum/viewtopic.php?p=16528#p16528

#include "HotA\homm3.h"

Patcher* _P; // required for every plugin
PatcherInstance* _PI;
static _bool_ plugin_On = 0;

int __stdcall Y_NewWeek(LoHook* h, HookContext* c)
{
   // artifacts from the merchant in the castle
   if (o_GameMgr->bMarketArt[0] == -1) o_GameMgr->bMarketArt[0] = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 2);
   if (o_GameMgr->bMarketArt[1] == -1) o_GameMgr->bMarketArt[1] = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 2);
   if (o_GameMgr->bMarketArt[2] == -1) o_GameMgr->bMarketArt[2] = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 2);
   if (o_GameMgr->bMarketArt[3] == -1) o_GameMgr->bMarketArt[3] = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 4);
   if (o_GameMgr->bMarketArt[4] == -1) o_GameMgr->bMarketArt[4] = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 4);
   if (o_GameMgr->bMarketArt[5] == -1) o_GameMgr->bMarketArt[5] = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 4);
   if (o_GameMgr->bMarketArt[6] == -1) o_GameMgr->bMarketArt[6] = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 8);

   // artifacts from merchants on the adventure map
   if (o_GameMgr->bMarketOnMap_first != 0 )
   {
      for (int i = o_GameMgr->bMarketOnMap_first; i <= o_GameMgr->bMarketOnMap_last; i += 28)
      {
         if (*(int*)(i + 0) == -1) *(int*)(i + 0) = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 2);
         if (*(int*)(i + 4) == -1) *(int*)(i + 4) = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 2);
         if (*(int*)(i + 8) == -1) *(int*)(i + 8) = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 2);
         if (*(int*)(i + 12) == -1) *(int*)(i + 12) = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 4);
         if (*(int*)(i + 16) == -1) *(int*)(i + 16) = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 4);
         if (*(int*)(i + 20) == -1) *(int*)(i + 20) = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 4);
         if (*(int*)(i + 24) == -1) *(int*)(i + 24) = CALL_2(int, __thiscall, 0x4C9190, o_GameMgr, 8);
      }
   }
   return EXEC_DEFAULT;
}

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.NewArtifactsFromMerchants_Serp");
            _PI->WriteLoHook(0x4C8446, Y_NewWeek);
        }
    }
    return TRUE;
}

 homm3.h
In homm3.h replace " _byte_ f1F644[84]; // +1F644h" with:
Код: Выделить всё
 _byte_ f1F644[32]; // +1F644h

 // artifacts from the merchant in the castles
 _dword_ bMarketArt[7]; // +1F664h

 // something with the merchants on the map (maybe a pointer to the address in save / load)
 _dword_ f1F680; // +1F680h

 // the first dealer of artifacts on the map in the list
 _dword_ bMarketOnMap_first; // +1F684h

  // the last dealer of artifacts on the map in the list
 _dword_ bMarketOnMap_last;  // +1F688h

 _byte_ f1F68C[12]; // +1F68Ch


@igrik: If I don't have permission to upload this plugin, please contact me and I will remove it.
Вложения
NewArtifactsMerchants.zip
(4.88 КБ) Скачиваний: 40
Вернуться к началу

offlineSerp
 
Сообщения: 8
Зарегистрирован: 05 окт 2017, 11:21
Поблагодарили: 13 раз.

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

Сообщение Serp » 06 окт 2017, 11:50

Authors:
igrik, Serp

Titel:
ManaRegenerationHeroLevel_Serp

Description:
Standard spell point regeneration is 1 + half hero_level now (rounded down).

Works also in Multiplayer. Every player should have this plugin installed/uninstalled, to not mess up the game.
I'm not sure if it works without HotA, not tested.
(I added _Serp to my mods to make sure they have a unique name, so there will be no conflicts)

 Code
Код: Выделить всё
// code from igrik http://handbookhmm.ru/forum/viewtopic.php?p=16558#p16558

#include "HotA\homm3.h"

Patcher* _P; // required for every plugin
PatcherInstance* _PI;
static _bool_ plugin_On = 0;

// The standard regeneration of the hero's mana is 1+half_level (instead of +1)
int __stdcall Y_New_BaseRestoreMana(LoHook* h, HookContext* c)
{
    c->ebx += (int)(((_Hero_*)c->edi)->level / 2); // we need += because ebx also contains mysticism. and normally, when msysticism is "2", this means 1 standard and 1 from mysticism. with adding the hero level, we will get mysticism value plus half hero level.
   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.ManaRegenerationHeroLevel_Serp");
           
            _PI->WriteLoHook(0x4E42D7, Y_New_BaseRestoreMana);
        }
    }
    return TRUE;
}


@igrik: If I don't have permission to upload this plugin, please contact me and I will remove it.
Вложения
ManaRegenerationHeroLevel_Serp.zip
(4.47 КБ) Скачиваний: 31
Вернуться к началу

offlineSerp
 
Сообщения: 8
Зарегистрирован: 05 окт 2017, 11:21
Поблагодарили: 13 раз.

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

Сообщение Serp » 06 окт 2017, 11:59

Authors:
RoseKavalier, AlexSpl, Serp

Titel:
NewMysticism_Serp

Description:
Mysticism regenerates 5%/10%/15% of max spell points per day.
But at least skill_level+1 (like default mysticism did).
Also adjusted secondary skill description accordingly.

Works also in Multiplayer. Every player should have this plugin installed/uninstalled, to not mess up the game.
I'm not sure if it works without HotA, not tested.
(I added _Serp to my mods to make sure they have a unique name, so there will be no conflicts)

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

Patcher* _P; // required for every plugin
PatcherInstance* _PI;
static _bool_ plugin_On = 0;

struct SecSkillDesc
{
   char* Name;
   char* BasicDesc;
   char* AdvancedDesc;
   char* ExpertDesc;
};

SecSkillDesc MysticismDesc =
{
   "{Mysticism}",
   "{Basic Mysticism}\n\nallows your hero to regenerate {5%} spell points per day.",
   "{Advanced Mysticism}\n\nallows your hero to regenerate {10%} spell points per day.",
   "{Expert Mysticism}\n\nallows your hero to regenerate {15%} spell points per day."
};

int __stdcall changeMysticismDesc(LoHook* h, HookContext* c)
{
   *(SecSkillDesc*)(*(int*)0x67DCF0 + 8 * 16) = MysticismDesc; // Mysticism has id 8 in homm3_ids
   return EXEC_DEFAULT;
}

int __stdcall NewMysticism(LoHook* h, HookContext* c)
{
    _Hero_* hero = (_Hero_*)c->edi; // grab current hero structure
    int skill_level = hero->second_skill[HSS_MYSTICISM]; // current skill level
    int hero_level = hero->level;
    short maxSpellPoints = (short)(10 * hero->knowledge * CALL_1(float, __thiscall, 0x4E4B20, hero)); // is only correct as long as knowledge is the only factor and not changed with other plugins
    int points = (int)(maxSpellPoints/20*skill_level);
    if (points <= 3)
        points = skill_level+1; // get at least the normal mysticism values
    c->ebx = points; // in fact only this -1 is added by mysticism, the missing 1 is standard mana regeneration.
    c->return_address = 0x4E41CC;
    return NO_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.Mysticism_Serp");
           
            _PI->WriteLoHook(0x4E41C5, NewMysticism); // make it a percentage of total spell points
            //_PI->WriteDword(0x63E9C8, 0);
            //_PI->WriteDword(0x63E9C8 + 4, 10);
            //_PI->WriteDword(0x63E9C8 + 8, 25);
            //_PI->WriteDword(0x63E9C8 + 12, 50);
            _PI->WriteLoHook(0x4E6D77, changeMysticismDesc);
        }
    }
    return TRUE;
}
Вложения
NewMysticism_Serp.zip
(5.23 КБ) Скачиваний: 46
Вернуться к началу

offlineSerp
 
Сообщения: 8
Зарегистрирован: 05 окт 2017, 11:21
Поблагодарили: 13 раз.

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

Сообщение Serp » 06 окт 2017, 12:03

Authors:
RoseKavalier, AlexSpl, Serp

Titel:
EstatesLevelHero_Serp

Description:
A hero contributes 50/100/150 gold per hero_level per day.
Also adjusted secondary skill description accordingly.

Works also in Multiplayer. Every player should have this plugin installed/uninstalled, to not mess up the game.
I'm not sure if it works without HotA, not tested.
(I added _Serp to my mods to make sure they have a unique name, so there will be no conflicts)

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

Patcher* _P; // required for every plugin
PatcherInstance* _PI;
static _bool_ plugin_On = 0;

struct SecSkillDesc
{
   char* Name;
   char* BasicDesc;
   char* AdvancedDesc;
   char* ExpertDesc;
};

SecSkillDesc EstatesDesc =
{
    "{Estates}",
    "{Basic Estates}\n\na hero contributes {50 gold per level} per day to your cause.",
    "{Advanced Estates}\n\na hero contributes {100 gold per level} per day to your cause.",
    "{Expert Estates}\n\na hero contributes {150 gold per level} per day to your cause."
};

int __stdcall changeEstatesDesc(LoHook* h, HookContext* c)
{
   *(SecSkillDesc*)(*(int*)0x67DCF0 + 13 * 16) = EstatesDesc; // Estates has id 13 in homm3_ids
   return EXEC_DEFAULT;
}

int __stdcall newEstate(LoHook* h, HookContext* c)
{
    _Hero_* hero = (_Hero_*)c->esi; // grab current hero structure
    int skill_level = hero->second_skill[HSS_ESTATES]; // current skill level
    int hero_level = hero->level;
    int gold = skill_level*hero_level*50;
    c->eax = gold;
    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.EstatesLevelHero_Serp");
           
         _PI->WriteLoHook(0x4E4622, newEstate);
         _PI->WriteLoHook(0x4E6D77, changeEstatesDesc);
        }
    }
    return TRUE;
}
Вложения
EstatesLevelHero_Serp.zip
(4.66 КБ) Скачиваний: 132
Вернуться к началу

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

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

Сообщение AlexSpl » 11 окт 2017, 14:14

Авторы: AlexSpl, Ben80, igrik

Название: newDisguise

Описание: Меняет длительность и эффект заклинания "Маскировка".

С помощью диалога любой игрок может выбрать, насколько сильной армия его героя, использующего заклинание "Маскировка", будет выглядеть при просмотре этого героя игроками по правому клику. При этом информация о численности отрядов в армии героя будет искажена: преуменьшена или преувеличена.

Доступны следующие опции:

На основном и базовом уровне Магии Воздуха - 66%, 150% (от количества войск в каждом отряде).
На продвинутом - 50%, 66%, 150%, 200%.
На экспертном - 25%, 33%, 50%, 66%, 150%, 200%, 300%, 500%.

Длительность заклинания - 2 хода игрока.

 Код
Код: Выделить всё
#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;

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

_Dlg_* __stdcall makeGhostHero(HiHook* hook, _Dlg_* dlg, _Hero_* hero, int a3)
{
    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 = ghostHero;
    }
       
    return CALL_3(_Dlg_*, __thiscall, hook->GetDefaultFunc(), dlg, hero, a3);
}

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;

    // Индекс множителя маскировки помещаем в старший полубайт,
    // длительность "Маскировки" (1 = 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, o_Spell[SPL_DISGUISE].wav_name);
        CALL_3(void, __thiscall, 0x59A7C0, -1, *(int*)0x6992A8, *(int*)0x6992AC);
    }
       
    c->return_address = 0x41C7F2;
    return NO_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(0x4C7DA5, "%n", 3);
            _PI->WriteCodePatch(0x41C7C6, "%n", 6);
            _PI->WriteCodePatch(0x41C7BA, "%n", 5);
            _PI->WriteLoHook(0x41C7DD, setDisguise);
            _PI->WriteLoHook(0x4C6CA9, decDisguise);
            // Если герой исключается из игры, не забываем уменьшить длительность Disguise для следующего
            _PI->WriteLoHook(0x4F1865, decDisguise);
            _PI->WriteHiHook(0x52EFB0, SPLICE_, EXTENDED_, THISCALL_, makeGhostHero);
        }
    }

    return TRUE;
}

newDisguise.zip
(7.47 КБ) Скачиваний: 44
Вернуться к началу

offlineBen80
Мастер
Мастер
 
Сообщения: 433
Зарегистрирован: 18 июн 2017, 06:49
Поблагодарили: 66 раз.

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

Сообщение Ben80 » 11 окт 2017, 20:14

Автор: RoseKavalier

Название: SiegeArmorerFix

Описание: Фикс бага - в оригинальной игре (SoD) вторичный навык Оружейник (Защита) осаждающего героя приводил к увеличению ущерба, наносимого его отрядам стрелковыми башнями (должно быть уменьшение).

Плагин для SoD/Complete.

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

Patcher* _P;
PatcherInstance* _PI;

static _bool_ plugin_On = 0;

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

            _PI->WriteByte(0x41E3A4, 0x4D);
            _PI->WriteByte(0x41E4DF, 0x4D);
            _PI->WriteByte(0x465944, 0x4D);
        }
    }

   return TRUE;
}



SiegeArmorerFix.zip
(3.39 КБ) Скачиваний: 33
Последний раз редактировалось Ben80 12 окт 2017, 18:29, всего редактировалось 2 раз(а).
Вернуться к началу

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

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

Сообщение RoseKavalier » 12 окт 2017, 04:01

Author: RoseKavalier

Title: Castle_lighthouse

Description:
The Castle's Lighthouse building bonus is now only applied to the Castle's owner.

 Code
Код: Выделить всё
// note I used Linker, this is a multi-solution Project
#include "homm3.h"

Patcher* _P;
PatcherInstance* _PI;

int __stdcall castle_owner_check(LoHook *h, HookContext *c)
{
   _Town_ *town = (_Town_*)(c->ecx);
   _Hero_ *hero = *(_Hero_**)(c->ebp - 4); // _Hero_ is stored in temp variable [LOCAL.1]

   if (hero->owner_id == town->owner_id) // normal
      return EXEC_DEFAULT;
   
   c->return_address = 0x4E4D6C; // skip procedure
   return NO_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.Castle.Lighthouse");
         _PI->WriteLoHook(0x4E4D40, castle_owner_check);
      }
      break;

   case DLL_THREAD_ATTACH:
   case DLL_THREAD_DETACH:
   case DLL_PROCESS_DETACH:
      break;
   }
   return TRUE;
}
Вложения
Castle_lighthouse.zip
(4.32 КБ) Скачиваний: 134
Вернуться к началу

offlineBen80
Мастер
Мастер
 
Сообщения: 433
Зарегистрирован: 18 июн 2017, 06:49
Поблагодарили: 66 раз.

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

Сообщение Ben80 » 12 окт 2017, 15:05

Авторы: AlexSpl, Ben80, igrik

Название: NewDisguiseVision

Описание: Меняет длительность и эффект заклинания Маскировка. Кроме того, позволяет заклинанию Видение на экспертном уровне узнавать истинную численность армии, даже если была применена Маскировка. Заклинание Видение становится заклинанием Школы воды, изменены коэффициенты для расчета расстояния, на котором действует заклинание, вместо 1/2/3 - 2/2/2.
Алгоритм изменения поля disguise героя отличается от примененного в плагине NewDisguise, возможно, в сторону большей надежности.

Протестирован только для SoD/Complete.

С помощью диалога любой игрок может выбрать, насколько сильной армия его героя, использующего заклинание "Маскировка", будет выглядеть при просмотре этого героя игроками по правому клику. При этом информация о численности отрядов в армии героя будет искажена: преуменьшена или преувеличена.

Доступны следующие опции:

На основном и базовом уровне Магии Воздуха - 66%, 150% (от количества войск в каждом отряде).
На продвинутом - 50%, 66%, 150%, 200%.
На экспертном - 25%, 33%, 50%, 66%, 150%, 200%, 300%, 500%.

Длительность заклинания - 2 хода игрока.

 Код
Код: Выделить всё
#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;

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

int __stdcall setDisguiseDescription(LoHook* h, HookContext* c)
{

   return EXEC_DEFAULT;
}

int __stdcall setSpells(LoHook* h, HookContext* c)
{
   // Относим заклинание Видение к Школе воды,
   // меняем радиус действия в зависимости от Силы магии
   // теперь для любого уровня Магии воды коэффициент равен 2
   for(int i=0; i<4; i++)
      o_Spell[2].effect[i] = 2;
   o_Spell[2].school_flags = 4;
   // Меняем описание для Видения на экспертном уровне
   o_Spell[2].description[3] = \
      "{""Виденье"" 3 уровня}\nЕсли заклинание направляется на вражеский отряд, оно сообщает точное количество войск и говорит, захотят ли они присоединиться к вам. Позволяет увидеть значения основных навыков вражеского героя и тип и количество существ в его армии, как если бы это был ваш герой, даже если им была применена {Маскировка}. Также позволяет узнать характеристики близлежащего города, количество и вид находящихся в нем войск.\n";


   // Меняем описание для Маскировки
   for(int i=1; i<4; i++)
      o_Spell[4].description[i] = \
      "{""Маскировка""}\nДает ложную информацию вражескому герою, пытающемуся увидеть характеристики вашего героя.\n";

   return EXEC_DEFAULT;
}

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 (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;
   // Уровень Школы магии воды для героя, который смотрит другого героя
   // (в том числе с применением Видения)
   int heroVision;

   h->GetAddress() == ghostHeroHookAddr[0] ? heroReg = &c->esi : heroReg = &c->eax;
   h->GetAddress() == ghostHeroHookAddr[0] ? heroVision = *(int*)(c->ebp - 0x10) : heroVision = 0;

    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 )
            {
            // Эксперт Магии воды может получить правдивую информацию
            // об армии противника
            if (heroVision < 3)
               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}, select your army\nvisible strength.", hero->name);
    yDlgShow(320, 256, count[hero->second_skill[HSS_AIR_MAGIC]], "{Disguise}", Text);

    bool disguiseCast = o_WndMgr->result_dlg_item_id == 30722;

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

        // Расходуем ману
        CALL_2(int, __thiscall, 0x4D9540, hero, c->eax);
        // Проигрываем звук Disguise
      CALL_1(void, __thiscall, 0x59A770, o_Spell[4].wav_name);
        // Выполняем функцию, параметры для которой вернула функция воспроизведения WAV
      CALL_3(void, __thiscall, 0x59A7C0, -1, *(int*)0x6992A8, *(int*)0x6992AC);
    }
   
    c->return_address = 0x41C7F2;
    return NO_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.NewDisguiseVision");

         _PI->WriteCodePatch(0x41C7BA, "%n", 5);
         _PI->WriteCodePatch(0x4C7DA5, "%n", 3);
         _PI->WriteCodePatch(0x41C7C6, "%n", 6);

         _PI->WriteLoHook(0x4C6CD9, decDisguise);
         _PI->WriteLoHook(0x41C7DD, setDisguise);
         // Для Новой игры
         _PI->WriteLoHook(0x4BFB30, setSpells);
         // Для Загрузить игру
         _PI->WriteLoHook(0x4BEFF0, setSpells);

         for (int i = 0; i < 4; ++i) _PI->WriteLoHook(ghostHeroHookAddr[i], makeGhostHero);
        }
    }

   return TRUE;
}



Русская версия:
NewDisguiseVision_Rus.zip
(6.28 КБ) Скачиваний: 24


Английская версия:
NewDisguiseVision_Eng.zip
(6.18 КБ) Скачиваний: 39
Вернуться к началу

offlineBen80
Мастер
Мастер
 
Сообщения: 433
Зарегистрирован: 18 июн 2017, 06:49
Поблагодарили: 66 раз.

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

Сообщение Ben80 » 14 окт 2017, 15:26

Авторы: AlexSpl, Ben80

Название: NewBlind

Описание: Меняет длительность заклинания Ослепление в зависимости от уровня Магии огня - 1/3/5 раундов в зависимости от уровня навыка. Колдовская сила не влияет на длительность заклинания.
Дополнительно изменена процедура вычисления "ценности" заклинания AI, в которой учтены изменения заклинания.
Скорректировано описание заклинания.

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

Patcher* _P;
PatcherInstance* _PI;

static _bool_ plugin_On = 0;

int __stdcall setSpells(LoHook* h, HookContext* c)
{
   o_Spell[62].description[0] = \
   "{""Ослепление""}\n\nОслепляет существо, так что оно становится неспособно двигаться и отвечает на атаки вполсилы. Ослепление остается, пока отряд не атакован, или же чары не сняты. Продолжительность 1 раунд\n";
   o_Spell[62].description[1] = \
   "{""Ослепление"" 1 уровня}\n\nОслепляет существо, так что оно становится неспособно двигаться и отвечает на атаки вполсилы. Ослепление остается, пока отряд не атакован, или же чары не сняты. Продолжительность 1 раунд\n";
   o_Spell[62].description[2] = \
   "{""Ослепление"" 2 уровня}\n\nОслепляет существо, так что оно становится неспособно двигаться и отвечает на атаки вполсилы. Ослепление остается, пока отряд не атакован, или же чары не сняты. Продолжительность 3 раунда\n";
   o_Spell[62].description[3] = \
   "{""Ослепление"" 3 уровня}\n\nОслепляет существо, так что оно становится неспособно двигаться и отвечает на атаки вполсилы. Ослепление остается, пока отряд не атакован, или же чары не сняты. Продолжительность 5 раундов\n";
   
   return EXEC_DEFAULT;
}

int __stdcall setArgumentBlindAI(LoHook* h, HookContext* c)
{
   // В процедуре вычисления "ценности" заклинания "Ослепление" компьютером
   // есть две основных алгоритмических ветви, в одной из них
   // значение Колдовской силы не используется, а в другой используется,
   // хотя и далеко не всегда приводит к изменению конечно результата
   // (значению "ценности"). Поскольку в данном нововедении продолжительность
   // Ослепления не зависит от Колдовской силы, а зависит только от уровня
   // Магии огня, в начало процедуры установлен хук, который подменяет
   // значение аргумента, переданного процедуре - значение Колдовской силы
   // заменяет на 1/1/3/5 в зависимости от уровня Магии огня, то есть по сути
   // передается точно такое же актуальное значение продолжительности заклинания, как в
   // оригинальной игре.

   // Нет Магии огня
   if(*(int*)(c->ebp + 0x10) == 0)
      *(int*)(c->ebp + 0x18) = 1;
   // Основной уровень
   if(*(int*)(c->ebp + 0x10) == 1)
      *(int*)(c->ebp + 0x18) = 1;
   // Продвинутый уровень
   if(*(int*)(c->ebp + 0x10) == 2)
      *(int*)(c->ebp + 0x18) = 3;
   // Экспертный уровень
   if(*(int*)(c->ebp + 0x10) == 3)
      *(int*)(c->ebp + 0x18) = 5;

   return EXEC_DEFAULT;
}

int __stdcall blindDuration(LoHook* h, HookContext* c)
{
    if ( c->ebx == 62 )
    {
      int cmAddr = *(int*)0x699420;
      int monSide = *(int*)(c->esi + 244); // Сторона отряда, на который кастуется заклинание
      int hero = *(int*)(cmAddr + (1 - monSide) * 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)];
       // проверку hero можно опустить, т.к. ID действия равно 1 только тогда,
       // когда кастует именно герой. Если Ослепление кастуют единороги, то actionId = 6
       // (обычная атака или ответка врукопашной).
       // Если Ослепление будут кастовать какие-нибудь монстры
       // (например, сказочные драконы в одном из модов),
       // то всё равно будет работать, т.к. для каста монстров actionId = 10
    }
    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.NewBlind");
            _PI->WriteLoHook(0x438D58, setArgumentBlindAI);
            _PI->WriteLoHook(0x44467E, blindDuration);
            // Для Новой игры
            _PI->WriteLoHook(0x4BFB30, setSpells);
            // Для Загрузить игру
            _PI->WriteLoHook(0x4BEFF0, setSpells);
        }
    }

   return TRUE;
}



NewBlind.zip
(3.78 КБ) Скачиваний: 33


v.2
Описание: В оригинальном алгоритме при Колдовской силе меньше 2 "ценность" заклинания имеет завышенное значение, что в целом не страшно, поскольку такая ситуация - редкость.
В данном плагине такая ситуация нередка, поскольку эквивалент Колдовской силы равен 1 часто (может быть больше 1, только если развит навык Магии огня). Поэтому введена корректировка.

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

Patcher* _P;
PatcherInstance* _PI;

static _bool_ plugin_On = 0;

int __stdcall setSpells(LoHook* h, HookContext* c)
{
    o_Spell[62].description[0] = \
    "{""Ослепление""}\n\nОслепляет существо, так что оно становится неспособно двигаться и отвечает на атаки вполсилы. Ослепление остается, пока отряд не атакован, или же чары не сняты. Продолжительность 1 раунд\n";
    o_Spell[62].description[1] = \
    "{""Ослепление"" 1 уровня}\n\nОслепляет существо, так что оно становится неспособно двигаться и отвечает на атаки вполсилы. Ослепление остается, пока отряд не атакован, или же чары не сняты. Продолжительность 1 раунд\n";
    o_Spell[62].description[2] = \
    "{""Ослепление"" 2 уровня}\n\nОслепляет существо, так что оно становится неспособно двигаться и отвечает на атаки вполсилы. Ослепление остается, пока отряд не атакован, или же чары не сняты. Продолжительность 3 раунда\n";
    o_Spell[62].description[3] = \
    "{""Ослепление"" 3 уровня}\n\nОслепляет существо, так что оно становится неспособно двигаться и отвечает на атаки вполсилы. Ослепление остается, пока отряд не атакован, или же чары не сняты. Продолжительность 5 раундов\n";
   
    return EXEC_DEFAULT;
}

int __stdcall setArgumentBlindAI(LoHook* h, HookContext* c)
{
    // В процедуре вычисления "ценности" заклинания "Ослепление" компьютером
    // есть две основных алгоритмических ветви, в одной из них
    // значение Колдовской силы не используется, а в другой используется,
    // хотя и далеко не всегда приводит к изменению конечно результата
    // (значению "ценности"). Поскольку в данном нововедении продолжительность
    // Ослепления не зависит от Колдовской силы, а зависит только от уровня
    // Магии огня, в начало процедуры установлен хук, который подменяет
    // значение аргумента, переданного процедуре - значение Колдовской силы
    // заменяет на 1/1/3/5 в зависимости от уровня Магии огня, то есть по сути
    // передается точно такое же актуальное значение продолжительности заклинания, как в
    // оригинальной игре.

    // Нет Магии огня
    if(*(int*)(c->ebp + 0x10) == 0)
        *(int*)(c->ebp + 0x18) = 1;
    // Основной уровень
    if(*(int*)(c->ebp + 0x10) == 1)
        *(int*)(c->ebp + 0x18) = 1;
    // Продвинутый уровень
    if(*(int*)(c->ebp + 0x10) == 2)
        *(int*)(c->ebp + 0x18) = 3;
    // Экспертный уровень
    if(*(int*)(c->ebp + 0x10) == 3)
        *(int*)(c->ebp + 0x18) = 5;

   return EXEC_DEFAULT;
}

int __stdcall setDividerBlindAI(LoHook* h, HookContext* c)
{
    if(((int)(c->eax)) < 2)
        c->eax = 2;

    return EXEC_DEFAULT;
}

int __stdcall blindDuration(LoHook* h, HookContext* c)
{
    if ( c->ebx == 62 )
    {
        int cmAddr = *(int*)0x699420;
        int monSide = *(int*)(c->esi + 244); // Сторона отряда, на который кастуется заклинание
        int hero = *(int*)(cmAddr + (1 - monSide) * 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)];
        // проверку hero можно опустить, т.к. ID действия равно 1 только тогда,
        // когда кастует именно герой. Если Ослепление кастуют единороги, то actionId = 6
        // (обычная атака или ответка врукопашной).
        // Если Ослепление будут кастовать какие-нибудь монстры
        // (например, сказочные драконы в одном из модов),
        // то всё равно будет работать, т.к. для каста монстров actionId = 10
    }
    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.NewBlind");
            _PI->WriteLoHook(0x438D58, setArgumentBlindAI);
            _PI->WriteLoHook(0x44467E, blindDuration);
            // Для Новой игры
            _PI->WriteLoHook(0x4BFB30, setSpells);
            // Для Загрузить игру
            _PI->WriteLoHook(0x4BEFF0, setSpells);
            _PI->WriteLoHook(0x438DEA, setDividerBlindAI);
        }
    }

   return TRUE;
}



NewBlind.zip
(3.91 КБ) Скачиваний: 31


v. 3
Описание: Меняет длительность заклинания Ослепление в зависимости от уровня Магии огня - 2/3/4 раунда в зависимости от уровня навыка.
NewBlind.zip
(3.78 КБ) Скачиваний: 30
Вернуться к началу

offlineBen80
Мастер
Мастер
 
Сообщения: 433
Зарегистрирован: 18 июн 2017, 06:49
Поблагодарили: 66 раз.

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

Сообщение Ben80 » 28 окт 2017, 06:54

Автор: AlexSpl, Ben80

Название: NewMirthLeadershipStrongAI

Описание: Заклинание Радость на любом уровне действует сразу на все дружественные отряды. При этом мораль повышается на 1, и (если итоговая мораль положительна) дополнительно увеличивается вероятность выпадения боевого духа - на 50/50/75/100% от бонуса, связанного с моралью, в зависимости от уровня Магии воды. Например, при изначальной морали, равной 3, после применения заклинания Радость экспертного уровня вероятность выпадения боевого духа будет равна не 12.5%, а 25%.
Кроме того, вторичный навык Лидерство кроме повышения морали теперь также дополнительно увеличивает вероятность выпадения боевого духа - на 10/20/30% от бонуса, связанного с моралью. Например, при морали, равной 3, имея данный навык на экспертном уровне, вероятность выпадения боевого духа будет равна не 12.5%, а 16.25%.

Полностью переработана процедура оценки ценности заклинания AI, определяющая вероятность применения в бою этого заклинания AI.
Протестирован для SoD/Complete.

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

// объекты patcher_x86.
Patcher* _P;
PatcherInstance* _PI;

int __stdcall changeSpellDesc(LoHook* h, HookContext* c)
{
   o_Spell[49].description[0] = \
   "{Mirth}\n\nIncreases the probability of extra attacks of all friendly units.\n";
   o_Spell[49].description[1] = \
   "{Basic Mirth}\n\nIncreases the probability of extra attacks of all friendly units.\n";
   o_Spell[49].description[2] = \
   "{Advanced Mirth}\n\nIncreases the probability of extra attacks of all friendly units. Effect is bigger that of Basic Mirth.\n";
   o_Spell[49].description[3] = \
   "{Expert Mirth}\n\nIncreases the probability of extra attacks of all friendly units. Effect is twice that of Basic Mirth.\n";
   
   return EXEC_DEFAULT;
}

int __stdcall mirthAI(LoHook* h, HookContext* c)
{
    int currentSide = o_BattleMgr->current_side;
    int enemyDefense = o_BattleMgr->hero[1 - currentSide]->defence;
    int ourAttack = o_BattleMgr->hero[currentSide]->attack;
    int ourPower = o_BattleMgr->hero[currentSide]->power;

    // duration of our spell including duration bonus (due to artifacts and so on)
    int duration = ourPower + CALL_1(signed int, __thiscall, 0x4E5020, o_BattleMgr->hero[currentSide]);
    // AI always calculates relation of armies strength or probable duration of battle
    int probableBattleDuration = *(int*)(c->ecx + 0x3C);

    if(probableBattleDuration > 10)
        probableBattleDuration = 10;
    // we will use "actual" duration of our spell - when spell will be really useful
    if(duration > probableBattleDuration)
        duration = probableBattleDuration;
    // the more actual duration of spell the bigger spell effect overall ("weight" of spell)
    double durationMultiplier = pow((double)duration, 0.8);
    double effectMultiplier = 1.0;
    double coeff = 0.25;
    int weight = 0;

    _BattleStack_ monster = *(_BattleStack_*)(c->esi);
    // get monster strength very about, the bigger strength - the bigger spell "weight"
    int thisMonsterAIValue = (int)((((double)monster.count_current - 1.0 + ((double)monster.full_hp - (double)monster.lost_hp)/(double)monster.full_hp)) * monster.creature.AI_value);
    if(ourAttack - enemyDefense > 0)
        thisMonsterAIValue = (int)(thisMonsterAIValue * (1.0 + 0.045 * (ourAttack - enemyDefense)));
    else
        thisMonsterAIValue = (int)(thisMonsterAIValue * (1.0 + 0.030 * (ourAttack - enemyDefense)));       

    int morale = *(int*)(c->esi + 0x4E8);
    if(morale < -3)
        morale = -3;

    int levelSchool = o_BattleMgr->hero[currentSide]->second_skill[16];

    // when spell is applied - the probability of morale bonus growth
    // plus 50/50/75/100 percentage of current probability
    // depending on your level of Water magic
    // (for example if we have morale = 3 and probability = 12.5% then
    // if we Expert of Water magic we will have 25% of probability)
    double arrEffect[4] = {0.5, 0.5, 0.75, 1.0};
    double effect = arrEffect[levelSchool];

    // try to transform probability to coefficient for army strength
    // 1th element of array - for morale -3
    double probEffect[7] = {0.76, 0.84, 0.92, 1.0, 1.04, 1.08, 1.12};
    if(morale < 0)
        effectMultiplier = (probEffect[morale + 4] - probEffect[morale + 3])/ probEffect[morale + 3];
    if((morale >= 0) && (morale < 3))
        effectMultiplier = ((probEffect[morale + 4] + (probEffect[morale + 4] - 1.0) * effect) \
        - probEffect[morale + 3]) / probEffect[morale + 3];
    if(morale >= 3)
        effectMultiplier = ((probEffect[6] + (probEffect[6] - 1.0) * effect) - probEffect[6]) / probEffect[6];

    // monsters flags
    int canFly = (int)(monster.creature.flags >> 1);
    int tired = (int)(monster.creature.flags >> 26);
    int neutralMoral = (int)(monster.creature.flags >> 17);

    // probably for such creatures all this procedure will never be applied
    // still we make test - is creature always have neutral moral
    if ((neutralMoral % 2) == 1)
        effectMultiplier = 0.0;
   
    // we use two separate algorithms for seriuos siege combat (with Citadel or Castle)
    // and for simple battle
   
    // for simple battle
    if(o_BattleMgr->siege_kind2 == 0 || o_BattleMgr->siege_kind2 == 1)
    {
        // if creature-shooter tired we reduce effect
        if(monster.CanShoot() && tired % 2 == 1)
            if(durationMultiplier >= 1.0)
                durationMultiplier = durationMultiplier - 1.0;
            else
                durationMultiplier = 0.0;
        // if creature-non shooter tired or can't attack we reduce effect
        if(monster.CanShoot() == false)
        {
            if(tired % 2 == 1 || *(int*)(c->esi + 0x544)==0)
                if(durationMultiplier >= 1.0)
                    durationMultiplier = durationMultiplier - 1.0;
                else
                    durationMultiplier = 0.0;
        }
    }
    // for seriuos siege combat
    else
    {
        // if creature-shooter tired we reduce effect
        if(monster.CanShoot() && tired % 2 == 1)
            if(durationMultiplier >= 1.0)
                durationMultiplier = durationMultiplier - 1.0;
            else
                durationMultiplier = 0.0;

        if(monster.CanShoot() == false)
        {
            // if creature-non shooter can't fly and can't attack
            // we reduce effect to 0
            if(canFly % 2 == 0 && *(int*)(c->esi + 0x544) == 0)
                durationMultiplier = 0.0;
            // if creature-non shooter can fly or attack
            // but tired we reduce effect
            else
                if(tired % 2 == 1)
                    if(durationMultiplier >= 1.0)
                        durationMultiplier = durationMultiplier - 1.0;
                    else
                        durationMultiplier = 0.0;
        }
    }
    weight = (int)(thisMonsterAIValue * effectMultiplier * durationMultiplier * coeff);
           
    c->eax = weight;
    c->return_address = 0x437DDD;
    return NO_EXEC_DEFAULT;
}

int __stdcall massSpells(LoHook* h, HookContext* c)
{
      if(c->ecx == 49)
      {
        c->return_address = 0x59E387;
        return NO_EXEC_DEFAULT;
      }

      return EXEC_DEFAULT;             
}

int __stdcall mirthMoraleBonus(LoHook* h, HookContext* c)
{
   c->edi = 1;
   return EXEC_DEFAULT;
}

int __stdcall mirthAction(LoHook* h, HookContext* c)
{
   int cmAddr = *(int*)0x699420; // Получаем адрес Combat Manager
   int monSide = *(int*)(c->edi + 244); // Получаем сторону, на которой сражается монстр
   int heroOffset = *(int*)(cmAddr + monSide * 4 + 0x53CC); // Получаем адрес героя, наложившего Mirth

   if ( !heroOffset ) return EXEC_DEFAULT;

   char lvlWaterMagic = *(char*)(heroOffset + 201 + 16);
   char lvlLeadership = *(char*)(heroOffset + 201 + 6);
   
   if ( *(int*)(c->edi + 408 + 49 * 4) ) // Если "висит" Mirth (#49)
   {
      // prob bonus for spell Mirth + basic prob
      double k1[] = {6.24, 6.24, 7.28, 8.32};
      // prob bonus for leadership skill
      // double k2[] = {0.00, 0.42, 0.83, 1.25};
      double k2[] = {0.00, 0.20, 0.62, 1.25};
      unsigned int number;
      rand_s(&number); // Пользуемся rand_s, чтобы не "сломать" ГПСЧ игры
      double dice = (double) number / ((double) UINT_MAX + 1) * 100.0;

      // *(int*)c->ebx - Мораль отряда
      int morale = *(int*)c->ebx;
      if(morale > 3)
          morale = 3;
      double edge = (k1[lvlWaterMagic] + k2[lvlLeadership]) * morale;
      if ( dice <= edge)
        c->return_address = 0x4645C0;
      else
        c->return_address = 0x464708;
      return NO_EXEC_DEFAULT;

   }
   
   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.NewMirthLeadershipStrongAI");
           
            _PI->WriteLoHook(0x444967, mirthMoraleBonus);
            _PI->WriteLoHook(0x4645BA, mirthAction);
            _PI->WriteLoHook(0x59E360, massSpells);
            _PI->WriteLoHook(0x437DCA, mirthAI);
            _PI->WriteLoHook(0x59E437, changeSpellDesc);
        }
        break;

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



Русская версия:
NewMirth_Rus.zip
(4.93 КБ) Скачиваний: 23


Английская версия:
NewMirth_Eng.zip
(4.9 КБ) Скачиваний: 39


v. 2
Описание: Бонус к навыку Лидерство - 5/15/30% (вместо 10/20/30%).
NewMirth_Eng_v2.zip
(4.91 КБ) Скачиваний: 33


v. 2.1
Описание: Баг фикс для Мастер-джиннов
NewMirth_Eng_v2.1.zip
(4.9 КБ) Скачиваний: 22


v. 2.2
Описание: Баг фикс для Мастер-джиннов
NewMirth_Eng_v2.2.zip
(4.92 КБ) Скачиваний: 22


v. 2.3
Описание: Баг фикс для Мастер-джиннов
NewMirth_Eng_v2.3.zip
(5 КБ) Скачиваний: 51


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

// объекты patcher_x86.
Patcher* _P;
PatcherInstance* _PI;

int __stdcall changeSpellDesc(LoHook* h, HookContext* c)
{
   o_Spell[49].description[0] = \
   "{Mirth}\n\nIncreases the probability of extra attacks of all friendly units.\n";
   o_Spell[49].description[1] = \
   "{Basic Mirth}\n\nIncreases the probability of extra attacks of all friendly units.\n";
   o_Spell[49].description[2] = \
   "{Advanced Mirth}\n\nIncreases the probability of extra attacks of all friendly units. Effect is bigger that of Basic Mirth.\n";
   o_Spell[49].description[3] = \
   "{Expert Mirth}\n\nIncreases the probability of extra attacks of all friendly units. Effect is twice that of Basic Mirth.\n";
   
   return EXEC_DEFAULT;
}

int __stdcall mirthAI(LoHook* h, HookContext* c)
{
    int cmAddr = *(int*)0x699420;
    int enemyDefense = 0;
    int ourAttack = 0;
    int ourPower = 0;
    int duration = 0;
    int actionId = *(int*)(cmAddr + 0x3C);
    int currentSide = o_BattleMgr->current_side;


    if(o_BattleMgr->hero[1 - currentSide])
        int enemyDefense = o_BattleMgr->hero[1 - currentSide]->defence;
    if(o_BattleMgr->hero[currentSide])
    {
        ourAttack = o_BattleMgr->hero[currentSide]->attack;
        ourPower = o_BattleMgr->hero[currentSide]->power;
    }


    if( o_BattleMgr->hero[currentSide] && ( o_BattleMgr->GetCurrentStack()->creature_id != 37
    || (o_BattleMgr->GetCurrentStack()->creature_id == 37 && actionId != 3)) )
        duration = ourPower + CALL_1(signed int, __thiscall, 0x4E5020, o_BattleMgr->hero[currentSide]);
    else
        duration = 6;

    // AI always calculates relation of armies strength or probable duration of battle
    int probableBattleDuration = *(int*)(c->ecx + 0x3C);

    if(probableBattleDuration > 10)
        probableBattleDuration = 10;
    // we will use "actual" duration of our spell - when spell will be really useful
    if(duration > probableBattleDuration)
        duration = probableBattleDuration;
    // the more actual duration of spell the bigger spell effect overall ("weight" of spell)
    double durationMultiplier = pow((double)duration, 0.8);
    double effectMultiplier = 1.0;
    double coeff = 0.25;
    int weight = 0;

    _BattleStack_ monster = *(_BattleStack_*)(c->esi);
    // get monster strength very about, the bigger strength - the bigger spell "weight"
    int thisMonsterAIValue = (int)((((double)monster.count_current - 1.0 + ((double)monster.full_hp - (double)monster.lost_hp)/(double)monster.full_hp)) * monster.creature.AI_value);
    if(ourAttack - enemyDefense > 0)
        thisMonsterAIValue = (int)(thisMonsterAIValue * (1.0 + 0.045 * (ourAttack - enemyDefense)));
    else
        thisMonsterAIValue = (int)(thisMonsterAIValue * (1.0 + 0.030 * (ourAttack - enemyDefense)));       

    int morale = *(int*)(c->esi + 0x4E8);
    if(morale < -3)
        morale = -3;

    int levelSchool = 0;
    if( o_BattleMgr->hero[currentSide] && ( o_BattleMgr->GetCurrentStack()->creature_id != 37
    || (o_BattleMgr->GetCurrentStack()->creature_id == 37 && actionId != 3)) )
        levelSchool = o_BattleMgr->hero[currentSide]->second_skill[16];
    else
        levelSchool = 2;

    // when spell is applied - the probability of morale bonus growth
    // plus 50/50/75/100 percentage of current probability
    // depending on your level of Water magic
    // (for example if we have morale = 3 and probability = 12.5% then
    // if we Expert of Water magic we will have 25% of probability)
    double arrEffect[4] = {0.5, 0.5, 0.75, 1.0};
    double effect = arrEffect[levelSchool];

    // try to transform probability to coefficient for army strength
    // 1th element of array - for morale -3
    double probEffect[7] = {0.76, 0.84, 0.92, 1.0, 1.04, 1.08, 1.12};
    if(morale < 0)
        effectMultiplier = (probEffect[morale + 4] - probEffect[morale + 3])/ probEffect[morale + 3];
    if((morale >= 0) && (morale < 3))
        effectMultiplier = ((probEffect[morale + 4] + (probEffect[morale + 4] - 1.0) * effect) \
        - probEffect[morale + 3]) / probEffect[morale + 3];
    if(morale >= 3)
        effectMultiplier = ((probEffect[6] + (probEffect[6] - 1.0) * effect) - probEffect[6]) / probEffect[6];

    // monsters flags
    int canFly = (int)(monster.creature.flags >> 1);
    int tired = (int)(monster.creature.flags >> 26);
    int neutralMoral = (int)(monster.creature.flags >> 17);

    // probably for such creatures all this procedure will never be applied
    // still we make test - is creature always have neutral moral
    if ((neutralMoral % 2) == 1)
        effectMultiplier = 0.0;
   
    // we use two separate algorithms for seriuos siege combat (with Citadel or Castle)
    // and for simple battle
   
    // for simple battle
    if(o_BattleMgr->siege_kind2 == 0 || o_BattleMgr->siege_kind2 == 1)
    {
        // if creature-shooter tired we reduce effect
        if(monster.CanShoot() && tired % 2 == 1)
            if(durationMultiplier >= 1.0)
                durationMultiplier = durationMultiplier - 1.0;
            else
                durationMultiplier = 0.0;
        // if creature-non shooter tired or can't attack we reduce effect
        if(monster.CanShoot() == false)
        {
            if(tired % 2 == 1 || *(int*)(c->esi + 0x544)==0)
                if(durationMultiplier >= 1.0)
                    durationMultiplier = durationMultiplier - 1.0;
                else
                    durationMultiplier = 0.0;
        }
    }
    // for seriuos siege combat
    else
    {
        // if creature-shooter tired we reduce effect
        if(monster.CanShoot() && tired % 2 == 1)
            if(durationMultiplier >= 1.0)
                durationMultiplier = durationMultiplier - 1.0;
            else
                durationMultiplier = 0.0;

        if(monster.CanShoot() == false)
        {
            // if creature-non shooter can't fly and can't attack
            // we reduce effect to 0
            if(canFly % 2 == 0 && *(int*)(c->esi + 0x544) == 0)
                durationMultiplier = 0.0;
            // if creature-non shooter can fly or attack
            // but tired we reduce effect
            else
                if(tired % 2 == 1)
                    if(durationMultiplier >= 1.0)
                        durationMultiplier = durationMultiplier - 1.0;
                    else
                        durationMultiplier = 0.0;
        }
    }
    weight = (int)(thisMonsterAIValue * effectMultiplier * durationMultiplier * coeff);
   
           
    c->eax = weight;
    c->return_address = 0x437DDD;
    return NO_EXEC_DEFAULT;
}

int __stdcall massSpells(LoHook* h, HookContext* c)
{
    if(c->ecx == 49)
    {
        c->return_address = 0x59E387;
        return NO_EXEC_DEFAULT;
    }

    return EXEC_DEFAULT;               
}

int __stdcall mirthMoraleBonus(LoHook* h, HookContext* c)
{
    c->edi = 1;
    return EXEC_DEFAULT;
}

int __stdcall mirthAction(LoHook* h, HookContext* c)
{
    int cmAddr = *(int*)0x699420; // Получаем адрес Combat Manager
    int monSide = *(int*)(c->edi + 244); // Получаем сторону, на которой сражается монстр
    int actionId = *(int*)(cmAddr + 0x3C);
    int heroOffset = *(int*)(cmAddr + monSide * 4 + 0x53CC); // Получаем адрес героя, наложившего Mirth

    char lvlWaterMagic = 0;
    char lvlLeadership = 0;

   
    if(o_BattleMgr->hero[monSide] && actionId != 10)
    {
        lvlWaterMagic = *(char*)(heroOffset + 201 + 16);
        lvlLeadership = *(char*)(heroOffset + 201 + 6);
    }
    else
    {
        lvlWaterMagic = 2;
        lvlLeadership = 0;
    }
   
    if ( *(int*)(c->edi + 408 + 49 * 4) ) // Если "висит" Mirth (#49)
    {
        // prob bonus for spell Mirth + basic prob
        double k1[] = {6.24, 6.24, 7.28, 8.32};
        // prob bonus for leadership skill
        double k2[] = {0.00, 0.20, 0.62, 1.25};
        unsigned int number;
        rand_s(&number); // Пользуемся rand_s, чтобы не "сломать" ГПСЧ игры
        double dice = (double) number / ((double) UINT_MAX + 1) * 100.0;

        // *(int*)c->ebx - Мораль отряда
        int morale = *(int*)c->ebx;
        if(morale > 3)
            morale = 3;
        double edge = (k1[lvlWaterMagic] + k2[lvlLeadership]) * morale;
        if ( dice <= edge)
            c->return_address = 0x4645C0;
        else
            c->return_address = 0x464708;
        return NO_EXEC_DEFAULT;

    }
   
    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.NewMirthLeadershipStrongAI");
           
            _PI->WriteLoHook(0x444967, mirthMoraleBonus);
            _PI->WriteLoHook(0x4645BA, mirthAction);
            _PI->WriteLoHook(0x59E360, massSpells);
            _PI->WriteLoHook(0x437DCA, mirthAI);
            _PI->WriteLoHook(0x59E437, changeSpellDesc);
        }
        break;

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

Последний раз редактировалось Ben80 08 ноя 2017, 03:23, всего редактировалось 4 раз(а).
Вернуться к началу

Пред.След.

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

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

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

cron