Объявления

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

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

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

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

Сообщение Ben80 » 01 сен 2019, 07:08

Автор: Ben80

Название: Battle_AI

Описание: Отличие от оригинала будет заметно сильным игрокам и не слишком заметно - слабым. Улучшенные ИИ монстры менее успешно отвлекаются слабыми и быстрыми стеками противника, чем в оригинале - если их нельзя ударить сразу (ИИ пытается определить - "развод" ли это, и если "развод" - то отвлекающие стеки рассматриваются как стоящие на 1-2 хода дальше, чем это есть на самом деле).

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

Patcher* _P;
PatcherInstance* _PI;

static _bool_ plugin_On = 0;

int getPenalty(_BattleStack_* AIStack, _BattleStack_* targetStack)
{
   int targetSpeed = CALL_1(int, __thiscall, 0x4489F0, targetStack);
   int AISpeed = CALL_1(int, __thiscall, 0x4489F0, AIStack);

   int penalty = 0;

   // коэффициент "полезности" цели
   // находим его упрощенно
   double targetCoef = sqrt((double)(targetStack->creature.AI_value * 0.07 / targetStack->full_hp));

   int damage = CALL_6(int, __thiscall, 0x4424A0, AIStack, targetStack, 0, AIStack->count_current, 0, 0);

   if((AIStack->creature.flags & BCF_ATTACK_TWICE) && !(AIStack->creature.flags & BCF_CAN_SHOOT))
      damage *= 2;

   // Есть неоднозначность, как считать нанесенный ущерб, если
   // атакуемый стек имеет неполное здоровье
   // в данном коде мы делаем следующим образом
   int stackFullHP = targetStack->full_hp * targetStack->count_current;
   int damageReal;

   int lostHpEffective = targetStack->lost_hp * targetCoef / 4;
   if(lostHpEffective > targetStack->lost_hp)
      lostHpEffective = targetStack->lost_hp;

   int restDamage = damage - (targetStack->full_hp - targetStack->lost_hp);
   if(restDamage < 0)
      damageReal = damage;
   else
   {
      if(restDamage >= stackFullHP - targetStack->full_hp)
         restDamage = stackFullHP - targetStack->full_hp;
      damageReal = restDamage + (targetStack->full_hp - targetStack->lost_hp) + lostHpEffective;
   }

   if(targetStack->creature.flags & BCF_CLONE)
      damageReal = stackFullHP / 5;

   double damageRelation = (double)damage /
      (targetCoef * (double)(damageReal));

   if(damageRelation > 2.5)
      penalty++;

   if(targetSpeed > 10)
      targetSpeed = 10;

   if(targetStack->creature.flags & BCF_CAN_FLY)
      targetSpeed = ceil((double)targetSpeed * 1.2);
   if(AIStack->creature.flags & BCF_CAN_FLY)
      AISpeed = ceil((double)AISpeed * 1.2);
   if(targetStack->creature.flags & BCF_2HEX_WIDE)
      targetSpeed = targetSpeed - 1;

   int speedMax = AISpeed + 3;
   if(AISpeed > 6)
      speedMax = AISpeed + 4;

   if(targetSpeed > speedMax)
      penalty++;

   return penalty;
}

int __stdcall addAIMeleePenalty(LoHook* h, HookContext* c)
{
   _BattleStack_* bestStack = *(_BattleStack_**)(c->ebp - 8);
   _BattleStack_* currentStack = *(_BattleStack_**)(c->ebp - 12);
   _BattleStack_* AIStack = *(_BattleStack_**)(c->ebp + 8);

   if(c->esi > 1)
      c->esi = c->esi + getPenalty(AIStack, currentStack);
   if(c->eax > 1)
      c->eax = c->eax + getPenalty(AIStack, bestStack);

   return EXEC_DEFAULT;
}

int __stdcall AI_MeleeBugFix(LoHook* h, HookContext* c)
{
   c->eax = c->eax * *(int*)(c->ebp - 0x24);

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

         _PI->WriteLoHook(0x421D30, addAIMeleePenalty);

         if(!(_P->GetInstance("HD.Plugin.AI_MeleeBugFix")))
            _PI->WriteLoHook(0x421D3F, AI_MeleeBugFix);

        }
    }

   return TRUE;
}



Версия 1.4:
Battle_AI v1.4.zip
(4.24 КБ) Скачиваний: 483
Вернуться к началу

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

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

Сообщение Ben80 » 21 июн 2020, 16:08

Автор: Ben80

Название: ViewMagicGuild

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

ViewMagicGuild.zip
(3.35 КБ) Скачиваний: 231


Код: Выделить всё
...
_PI->WriteByte(0x5CE98B+1, 0x1C);
Вернуться к началу

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

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

Сообщение AlexSpl » 14 окт 2020, 18:38

Автор: AlexSpl

Название: BadLuck

Описание: Плагин обеспечивает поддержку отрицательной Удачи (уполовинивание базового урона, анимация и звук). При желании вы также можете распаковать архив из этого сообщения в папку _HD3_Data\Common (def-файлы с картинками для отрицательной Удачи из HotA).

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

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

int __stdcall BadLuckAnim(LoHook* h, HookContext* c)
{
    int Luck = c->eax;
       
    if ( Luck < 0 )
    {
        if ( Luck < -3 ) Luck = -3;
        // RandomInt(1, 12);
        if ( CALL_2(int, __fastcall, 0x50C7C0, 1, 12) <= -Luck )
        {
            _BattleStack_* stack = (_BattleStack_*)c->esi;
            // Setting Luck flag to Bad Luck state (-1)
            stack->field_70 = -1;
            // Is battle visible?
            if ( !CALL_1(bool, __thiscall, 0x46A080, o_BattleMgr) )
            {
                // Play sound
                CALL_3(void, __fastcall, 0x59A890, (const char*)"badluck.82m", -1, 3);
                // Show message
                sprintf(o_TextBuffer, o_GENRLTXT_TXT->GetString(45),
                    CALL_2(const char*, __fastcall, 0x43FE20, stack->creature_id, stack->count_current));
                CALL_4(void, __thiscall, 0x4729D0, o_BattleMgr->dlg, o_TextBuffer, 1, 0);
                // Play animation
                CALL_5(void, __thiscall, 0x4963C0, o_BattleMgr, 48, stack, 100, 0);
            }
        }
        c->eax = 0;
    }

    return EXEC_DEFAULT;
}

int __stdcall HalveBasicDamage(HiHook* hook, _BattleStack_* stack, int a1)
{
    int BasicDamage = CALL_2(int, __thiscall, hook->GetDefaultFunc(), stack, a1);
    if ( stack->field_70 == -1 )
    {
        BasicDamage /= 2;
        if ( BasicDamage < 1 ) BasicDamage = 1;
    }

    return BasicDamage;
}

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((char*)"HD.Plugin.BadLuck");
            _PI->WriteHexPatch(0x43F642, "90 90 90 90 90 90");
            _PI->WriteHexPatch(0x441524, "90 90 90 90 90 90");
            _PI->WriteLoHook(0x43F64E, BadLuckAnim);
            _PI->WriteLoHook(0x441530, BadLuckAnim);
            _PI->WriteHiHook(0x442E80, SPLICE_, EXTENDED_, THISCALL_, HalveBasicDamage);
        }
    }

    return TRUE;
}

BadLuck.zip
(3.65 КБ) Скачиваний: 176


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

Название: BadLuckClassic

Описание: Плагин обеспечивает поддержку отрицательной Удачи (вычитание половины базового урона, анимация и звук). При желании вы также можете распаковать архив из этого сообщения в папку _HD3_Data\Common (def-файлы с картинками для отрицательной Удачи из HotA).

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

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

int __stdcall BadLuckAnim(LoHook* h, HookContext* c)
{
    int Luck = *(int*)(c->esi + 0x4EC);
       
    if ( Luck < 0 )
    {
        if ( Luck < -3 ) Luck = -3;
        // RandomInt(1, 12);
        if ( CALL_2(int, __fastcall, 0x50C7C0, 1, 12) <= -Luck )
        {
            _BattleStack_* stack = (_BattleStack_*)c->esi;
            // Setting Luck flag to Bad Luck state (-1)
            stack->field_70 = -1;
            // Is battle visible?
            if ( !o_BattleMgr->ShouldNotRenderBattle() )
            {
                // Play sound
                CALL_3(void, __fastcall, 0x59A890, (const char*)"badluck.82m", -1, 3);
                // Show message
                sprintf(o_TextBuffer, o_GENRLTXT_TXT->GetString(45),
                    GetCreatureName(stack->creature_id, stack->count_current));
                CALL_4(void, __thiscall, 0x4729D0, o_BattleMgr->dlg, o_TextBuffer, 1, 0);
                // Play animation
                CALL_5(void, __thiscall, 0x4963C0, o_BattleMgr, 48, stack, 100, 0);
            }
        }
    }

    return EXEC_DEFAULT;
}

int __stdcall ApplyLuckBonuses(LoHook* h, HookContext* c)
{
    int BonusDamage = *(int*)(c->ebp - 0x10);
    int BasicDamage = *(int*)(c->ebp + 8);
    int LuckFlag = *(int*)(c->ebx + 0x70);

    switch ( LuckFlag ) {
        case -1: BonusDamage -= BasicDamage / 2; break;
        case 1:  BonusDamage += BasicDamage; break;
    }
   
    *(int*)(c->ebp - 0x10) = BonusDamage;

    c->return_address = 0x4430B5;
    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((char*)"HD.Plugin.BadLuckClassic");
            _PI->WriteCodePatch(0x43F635, "%n", 13);
            _PI->WriteCodePatch(0x44151D, "%n", 13);
            _PI->WriteLoHook(0x43F635, BadLuckAnim);
            _PI->WriteLoHook(0x44151D, BadLuckAnim);
            _PI->WriteLoHook(0x4430A3, ApplyLuckBonuses);
        }
    }

    return TRUE;
}

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

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

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

Сообщение AlexSpl » 03 ноя 2020, 12:07

Авторы: AlexSpl, Rolex

Название: NewFirstAidTent

Описание: Существенно усиливает палатку первой помощи. Без вторичного навыка "Первая помощь" (First Aid) палатка восстанавливает 50 ед. здоровья случайному дружественному отряду. На базовом уровне вторичного навыка "Первая помощь" палатка лечит все дружественные отряды, восстанавливая 100 ед. здоровья каждому; на продвинутом - все дружественные отряды и боевые машины, кроме себя, восстанавливая 200 ед. здоровья; на экспертном - все дружественные отряды и боевые машины, восстанавливая 300 ед. здоровья. Кроме того, изменены характеристики палатки: теперь она стоит 1500 золота, имеет защиту 10 и здоровье 300.

Исправлены вылеты при игре по сети. Спасибо Armageddets за баг-репорты.

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

Patcher* _P;
PatcherInstance* _PI;

#define AnimateStack (((bool(*)[20])&o_BattleMgr->Field<bool>(0x547C)))

struct HealingInfo
{
    _BattleStack_* stack;
    int removedDamage;
} healingInfo[21 + 1];

void showDebugInfo(const char* format, ...)
{
    va_list args;
    va_start(args, format);

    vsprintf(o_TextBuffer, format, args);
    b_MsgBox(o_TextBuffer, MBX_OK);

    va_end(args);
}

int __stdcall afterInit(LoHook* h, HookContext* c)
{
    o_pCreatureInfo[CID_FIRST_AID_TENT].hit_points = 300;
    o_pCreatureInfo[CID_FIRST_AID_TENT].defence = 10;
    o_pCreatureInfo[CID_FIRST_AID_TENT].cost.gold = 1500;
   
    return EXEC_DEFAULT;
}

bool getHealingInfo()
{
    bool needHealing = false;
   
    _Hero_* hero = o_BattleMgr->hero[o_BattleMgr->current_side];

    if ( hero && hero->second_skill[HSS_FIRST_AID] )
    {
        int n = 0;
               
        for (int i = 0; i < o_BattleMgr->stacks_count[o_BattleMgr->current_side]; ++i)
        {
            _BattleStack_* stack = &o_BattleMgr->stack[o_BattleMgr->current_side][i];
       
            if ( stack && !stack->is_killed )
            {
                bool isSubjectToBeHealed = false;
               
                if ( stack->lost_hp ) {
                    switch ( hero->second_skill[HSS_FIRST_AID] ) {
                        case 1: isSubjectToBeHealed = !(stack->creature.flags & 0x200040); break;
                        case 2: isSubjectToBeHealed = !(stack->creature.flags & 0x200000) && stack->creature_id != CID_FIRST_AID_TENT; break;
                        case 3: isSubjectToBeHealed = !(stack->creature.flags & 0x200000); break;
                    }
                }

                if ( isSubjectToBeHealed )
                {
                    needHealing = true;
                    healingInfo[n++].stack = stack;
                }
            }
        }
   
        healingInfo[n].stack = (_BattleStack_*)ID_NONE;
    }

    return needHealing;
}

int __stdcall needHealing(LoHook* h, HookContext* c)
{
    _Hero_* hero = o_BattleMgr->hero[o_BattleMgr->current_side];

    if ( hero && hero->second_skill[HSS_FIRST_AID] )
    {
        if ( getHealingInfo() )
        {
            // Даём алгоритму игры валидную цель для хила
            *(int*)(c->ebp - 8) = healingInfo[0].stack->index_on_side;
           
            c->return_address = 0x4738C3;
        }
        else
            c->return_address = 0x4738B3;
       
        return NO_EXEC_DEFAULT;
    }

    return EXEC_DEFAULT;
}

int __stdcall findTargetsToHeal(LoHook* h, HookContext* c)
{
    if ( o_BattleMgr->Field<int>(0x3C) == 0xB ) getHealingInfo();
   
    return EXEC_DEFAULT;
}

int __stdcall applyHealing(LoHook* h, HookContext* c)
{
    _Hero_* hero = o_BattleMgr->hero[o_BattleMgr->current_side];

    if ( hero && hero->second_skill[HSS_FIRST_AID] )
    {
        for (int i = 0; healingInfo[i].stack != (_BattleStack_*)ID_NONE; ++i)
        {
            _BattleStack_* stack = healingInfo[i].stack;
           
            if ( stack && !stack->is_killed )
            {
                if ( c->eax < stack->lost_hp ) {
                    healingInfo[i].removedDamage = c->eax;
                    stack->lost_hp -= c->eax;
                }
                else {
                    healingInfo[i].removedDamage = stack->lost_hp;
                    stack->lost_hp = 0;
                }
            }
        }

        c->return_address = 0x478555;
        return NO_EXEC_DEFAULT;
    }

    return EXEC_DEFAULT;
}

int __stdcall playAnimation(LoHook* h, HookContext* c)
{
    _Hero_* hero = o_BattleMgr->hero[o_BattleMgr->current_side];

    if ( hero && hero->second_skill[HSS_FIRST_AID] )
    {
        memset(AnimateStack, false, 40);
       
        // Пишем лог и проигрываем анимацию для всех вылеченных отрядов
        for (int i = 0; healingInfo[i].stack != (_BattleStack_*)ID_NONE; ++i)
        {
            _BattleStack_* stack = healingInfo[i].stack;

            if ( stack && !stack->is_killed )
            {
                sprintf(o_TextBuffer, o_GENRLTXT_TXT->GetString(415), GetCreatureName(CID_FIRST_AID_TENT, 1),
                    GetCreatureName(stack->creature_id, stack->count_current), healingInfo[i].removedDamage);

                CALL_4(void, __thiscall, 0x4729D0, o_BattleMgr->dlg, o_TextBuffer, 1, 0);
               
                // Отмечаем отряды, для которых необходима анимация
                AnimateStack[o_BattleMgr->current_side][healingInfo[i].stack->index_on_side] = true;
            }
        }

        // Проигрываем массовую анимацию
        CALL_4(void, __thiscall, 0x5A6AD0, o_BattleMgr, AnimateStack, 79, 0);
       
        // Ждём, пока воспроизведётся звук
        CALL_3(void, __thiscall, 0x59A7C0, -1, c->eax, c->edx);
       
        c->return_address = 0x478692;
        return NO_EXEC_DEFAULT;
    }
       
    return EXEC_DEFAULT;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    static bool plugin_On = false;

    if ( DLL_PROCESS_ATTACH == ul_reason_for_call )
    {
        if ( !plugin_On )
        {
            plugin_On = true;
            _P = GetPatcher();
            _PI = _P->CreateInstance((char*)"HD.Plugin.NewFirstAidTent");

            // Меняем параметры First Aid Tent
            _PI->WriteLoHook(0x4EE1C1, afterInit);

            // Меняем коэффициенты
            float firstAidCoefs[] = {1.0f, 3.0f, 7.0f, 11.0f};
            _PI->WriteDword(0x63EA98, (int&)firstAidCoefs[0]);
            _PI->WriteDword(0x63EA98 + 4, (int&)firstAidCoefs[1]);
            _PI->WriteDword(0x63EA98 + 8, (int&)firstAidCoefs[2]);
            _PI->WriteDword(0x63EA98 + 12, (int&)firstAidCoefs[3]);

            // Проверяем, нужен ли хил
            _PI->WriteLoHook(0x473865, needHealing);
            _PI->WriteLoHook(0x4794EB, findTargetsToHeal);

            // Убираем ручной контроль
            _PI->WriteCodePatch(0x4745FD, "%n", 10);

            // Делаем хил максимальным
            _PI->WriteCodePatch(0x47852C, "%n", 12);
                       
            // Хилим
            _PI->WriteLoHook(0x47852C, applyHealing);

            // Пишем лог и проигрываем анимацию
            _PI->WriteLoHook(0x478580, playAnimation);
        }
    }

    return TRUE;
}

FirstAidTent.zip
(4.29 КБ) Скачиваний: 176
Последний раз редактировалось AlexSpl 16 апр 2021, 23:01, всего редактировалось 4 раз(а).
Вернуться к началу

offlineRolex  
имя: Alex
Ветеран
Ветеран
 
Сообщения: 886
Зарегистрирован: 22 сен 2020, 18:58
Откуда: УКРАИНА
Пол: Мужчина
Поблагодарили: 49 раз.

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

Сообщение Rolex » 08 ноя 2020, 20:38

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

Название: ExchangeHeroesCastle

Описание: Исправленный плагин обмена армиями/артефактами между героями в городе по горячей клавише "E/У".

 Код
Код: Выделить всё
#define _CRT_SECURE_NO_WARNINGS
#include "stdafx.h"
#include "..\..\HotA\homm3.h"
#define HK_E 18
#define o_Executive (*(int*)0x699550)

bool inTownDlg;

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


struct _Mgr_
{
   DWORD *vTable;
   _Mgr_ *mgr1;
   _Mgr_ *mgr2;
   DWORD f_0C;
   DWORD f_10;
   char name[256];

   void setManagers(void *m1, void *m2)
   {
      mgr1 = (_Mgr_ *)m1;
      mgr2 = (_Mgr_ *)m2;
   }
};

char __stdcall Y_DlgTown_Proc(HiHook* hook, _TownMgr_* tm, _EventMsg_* klick)
{
   char res = CALL_2(char, __thiscall, hook->GetDefaultFunc(), tm, klick);
   inTownDlg = false;

   if (res) {
      if (klick->type == 2 && klick->subtype == HK_E) {
         int heroU_id = tm->town->up_hero_id;
         int heroD_id = tm->town->down_hero_id;

         if (heroU_id != -1 && heroD_id != -1) {
            _Hero_* heroU = o_GameMgr->GetHero(heroU_id);
            _Hero_* heroD = o_GameMgr->GetHero(heroD_id);

            inTownDlg = true;

            _Mgr_ *mgr = (_Mgr_*)o_TownMgr;
            mgr->setManagers(o_AdvMgr, o_WndMgr);

            mgr = (_Mgr_*)o_AdvMgr;
            mgr->setManagers(NULL, o_TownMgr);

            mgr = (_Mgr_*)o_WndMgr;
            mgr->setManagers(o_TownMgr, o_MouseMgr);

            CALL_3(void, __thiscall, 0x4AAA60, o_TownMgr, heroU, heroD);

            // Перерисовываем диалог города
            CALL_1(void, __thiscall, 0x5D5930, o_TownMgr);
            CALL_1(void, __thiscall, 0x5D5810, o_TownMgr);

            inTownDlg = false;

            return 1;
         }
      }
   }

   return res;
}


int __stdcall Y_Dlg_HeroesMeet(LoHook* h, HookContext* c)
{
   if (inTownDlg) { // пропускаем обновление экрана
      c->return_address = 0x4AAC2A;
      return NO_EXEC_DEFAULT;
   }
   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((char*)"HD.Plugin.ExchangeHeroesCastle");

         _PI->WriteHiHook(0x5D3640, SPLICE_, EXTENDED_, THISCALL_, Y_DlgTown_Proc);
         _PI->WriteLoHook(0x4AAC1B, Y_Dlg_HeroesMeet);      
      }
   }

   return TRUE;
}
Вложения
ExchangeHeroesCastle.zip
(4.62 КБ) Скачиваний: 197
Вернуться к началу

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

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

Сообщение AlexSpl » 16 апр 2021, 23:13

Авторы: AlexSpl, Rolex

Название: NewMagicMirror

Описание: Существенно усиливает заклинание Magic Mirror. На базовом уровне Магии воздуха или без неё целевой отряд способен отразить вражеское заклинание на самый сильный отряд противника с вероятностью 40%, на продвинутом - с вероятностью 50%, на экспертном уровне заклинание становится массовым: все дружественные отряды способны отразить вражеское заклинание на все или самые сильные отряды противника с вероятностью 50%. Отражение работает, в том числе, и для заклинаний, наносящих урон по площади, а также для магии, накладываемой при атаке следующими существами: Thunderbird, Unicorn, War Unicorn, Black Knight, Dread Knight, Mummy.

Плагин совместим с плагином NewSpells.

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

#define SPL_DEATH_CLOUD_NEW 82
#define SPL_INCINERATION 92

using namespace std;

Patcher* _P;
PatcherInstance* _PI;

struct _BattleStackEx_ : public _BattleStack_
{
   int getMonsterSpell();
   bool approvedTarget(const int spell);
   bool magicMirrorLucky(const int spell);
   _BattleStackEx_* getStackToRedirect(const int spell, const bool massCast = true);
   int calcMagicDamage(const int spell, const _Hero_* attHero, const _Hero_* defHero);
   float getFullVulnerability(const int spell);
     
   inline int current_health() {
       return this->count_current * this->full_hp - this->lost_hp;
   }
   inline int full_health() {
       return this->count_current * this->full_hp;
   }
   inline int fightValue() {
       return this->creature.fight_value * this->count_current;
   };
   inline double damageFightValue(const int spell, const int spellDamage) {
       return this->getFullVulnerability(spell) * min((double)spellDamage / this->full_hp,
           this->count_current - (double)this->lost_hp / this->full_hp) * this->creature.fight_value;
   }
   inline double extraFightValue(const int spell, const int spellDamage) {
       return spellDamage % this->full_hp >= this->full_hp - this->lost_hp && spellDamage < this->current_health() ?
           this->getFullVulnerability(spell) * this->creature.fight_value : 0;
   }
   inline float getMagicVulnerability(const int spell) {
       return CALL_7(float, __thiscall, 0x5A83A0, o_BattleMgr, spell, o_BattleMgr->current_side, this, 1, 1, 0);
   };
   inline int getHitChanceResist(const int spell) {
       return (int)(100 * getMagicVulnerability(spell));
   }
   inline int getHitChanceFull(const int spell) {
       return (int)(100 * getFullVulnerability(spell));
   }
   inline bool isImmuneTo(const int spell) {
       return getMagicVulnerability(spell) == 0;
   };
   inline void playAnimation(const int id) {
       CALL_5(void, __thiscall, 0x4963C0, o_BattleMgr, id, this, 100, 0);
   }
};

class cmpFightValue
{
   int spell;

public:
   cmpFightValue(int id) : spell(id) { }

   bool operator () (_BattleStackEx_* a, _BattleStackEx_* b)
   {
       return a->getFullVulnerability(spell) * a->fightValue() > b->getFullVulnerability(spell) * b->fightValue();
   }
};

class cmpDamageFightValue
{
   int spell;
   _Hero_* attHero;
   _Hero_* defHero;

public:
   cmpDamageFightValue(int id, _Hero_* aHero, _Hero_* dHero) : spell(id), attHero(aHero), defHero(dHero) { }

   bool operator () (_BattleStackEx_* a, _BattleStackEx_* b)
   {
       return a->damageFightValue(spell, a->calcMagicDamage(spell, attHero, defHero)) >
           b->damageFightValue(spell, b->calcMagicDamage(spell, attHero, defHero));
   }
};

class cmpExtraFightValue
{
   int spell;
   _Hero_* attHero;
   _Hero_* defHero;

public:
   cmpExtraFightValue(int id, _Hero_* aHero, _Hero_* dHero) : spell(id), attHero(aHero), defHero(dHero) { }

   bool operator () (_BattleStackEx_* a, _BattleStackEx_* b)
   {
       return a->extraFightValue(spell, a->calcMagicDamage(spell, attHero, defHero)) >
           b->extraFightValue(spell, b->calcMagicDamage(spell, attHero, defHero));
   }
};

class cmpSpellDuration
{
   int spell;

public:
   cmpSpellDuration(int id) : spell(id) { }

   bool operator () (_BattleStackEx_* a, _BattleStackEx_* b)
   {
       return a->active_spell_duration[spell] < b->active_spell_duration[spell];
   }
};

bool showMagicMirrorAnim, showMagicResistAnim, showSpellAnim;
bool redirectedSingleCast, redirectedAreaSpell;
bool showResistAfterAttack = false, showMagicMirrorAfterAttack = false;
bool needMagicMirrorAnim[2][20], needMagicResistAnim[2][20], needSpellAnim[2][20];
_BattleStackEx_* targetStack = 0;

vector<_BattleStackEx_*> stackVector;
int stackIndex;

#define SPL_DEATH_RIPPLE 24
#define CID_AZURE_DRAGON 132

#define AnimateStack (((bool(*)[20])&o_BattleMgr->Field<bool>(0x547C)))

enum A {A_REFLECTSPELL_HOOK, A_MASSREFLECTION_HOOK, A_REFLECTSPELL_SKIP, A_REFLECTSPELL_DEFAULT, A_MASSANIMATION};
enum E {E_MASS, E_BERSERK, E_AREA, E_DEATH_RIPPLE, E_DESTROY_UNDEAD, E_ARMAGEDDON, E_SIZE};

const _ptr_ hAddr[][E_SIZE] =
{
   {0x5A6A50, 0x5A20F4, 0x5A4D74, 0x5A1006, 0x5A1203, 0x5A4F4E}, // A_REFLECTSPELL_HOOK
   {0x5A13E5, 0x5A2156, 0x5A4C80, 0x5A1151, 0x5A12BD, 0x5A4FFF}, // A_MASSREFLECTION_HOOK
   {0x5A6A83, 0x5A210E, 0x5A4D89, 0x5A107B, 0x5A1278, 0x5A4FBB}, // A_REFLECTSPELL_SKIP
   {0x000000, 0x000000, 0x5A4DA8, 0x5A1020, 0x5A121D, 0x5A4F63}, // A_REFLECTSPELL_DEFAULT
   {0x5A13FC, 0x5A2168, 0x000000, 0x5A116F, 0x5A12E0, 0x000000}  // A_MASSANIMATION
};

struct SpellDesc
{
   char* DefaultDesc;
   char* BasicDesc;
   char* AdvancedDesc;
   char* ExpertDesc;
};

SpellDesc NewMagicMirrorRus =
{
   "{Магическое зеркало}\n\nЦелевой отряд способен отразить вражеское заклинание на самый сильный отряд противника с вероятностью в 40%.",
   "{Базовое Магическое зеркало}\n\nЦелевой отряд способен отразить вражеское заклинание на самый сильный отряд противника с вероятностью в 40%.",
   "{Продвинутое Магическое зеркало}\n\nЦелевой отряд способен отразить вражеское заклинание на самый сильный отряд противника с вероятностью в 50%.",
   "{Экспертное Магическое зеркало}\n\nВсе дружественные отряды способны отразить вражеское заклинание на все или самые сильные отряды противника с вероятностью в 50%."
};

SpellDesc NewMagicMirrorEng =
{
   "{Magic Mirror}\n\nThe target stack is able to reflect the enemy spell on the strongest enemy stack with a 40% chance.",
   "{Basic Magic Mirror}\n\nThe target stack is able to reflect the enemy spell on the strongest enemy stack with a 40% chance.",
   "{Advanced Magic Mirror}\n\nThe target stack is able to reflect the enemy spell on the strongest enemy stack with a 50% chance.",
   "{Expert Magic Mirror}\n\nAll friendly stacks are able to reflect an enemy spell on all or the most strongest enemy stacks with a 50% chance."
};

int __stdcall afterInit(LoHook* h, HookContext* c)
{
   o_Spell[SPL_MAGIC_MIRROR].effect[0] = 40;
   o_Spell[SPL_MAGIC_MIRROR].effect[1] = 40;
   o_Spell[SPL_MAGIC_MIRROR].effect[2] = 50;
   o_Spell[SPL_MAGIC_MIRROR].effect[3] = 50;

   *(SpellDesc*)o_Spell[SPL_MAGIC_MIRROR].description =
       o_CreatureInfo[0].name_single[0] != 'P' ? NewMagicMirrorRus : NewMagicMirrorEng;

   return EXEC_DEFAULT;
}

void showDebugInfo(const char* format, ...)
{
   va_list args;
   va_start(args, format);

   vsprintf(o_TextBuffer, format, args);
   b_MsgBox(o_TextBuffer, MBX_OK);

   va_end(args);
}

_ptr_ getRA(const int spell)
{
   _ptr_ retAddr;

   switch ( spell )
   {
   case SPL_FROST_RING:
   case SPL_FIREBALL:
   case SPL_INFERNO:
   case SPL_METEOR_SHOWER:
   case SPL_DEATH_CLOUD_NEW:
   case SPL_INCINERATION:
       retAddr = hAddr[A_REFLECTSPELL_DEFAULT][E_AREA];
       break;
   case SPL_DEATH_RIPPLE:
       retAddr = hAddr[A_REFLECTSPELL_DEFAULT][E_DEATH_RIPPLE];
       break;
   case SPL_DESTROY_UNDEAD:
       retAddr = hAddr[A_REFLECTSPELL_DEFAULT][E_DESTROY_UNDEAD];
       break;
   case SPL_ARMAGEDDON:
       retAddr = hAddr[A_REFLECTSPELL_DEFAULT][E_ARMAGEDDON];
       break;
   }
 
   return retAddr;
}

bool _BattleStackEx_::approvedTarget(const int spell)
{
   return !this->is_killed && !this->isImmuneTo(spell) && !(spell == SPL_FORGETFULNESS && this->creature.shots == 0);
}

int getEffSchoolLevel(const _Hero_* hero, const int spell)
{
   return CALL_3(int, __thiscall, 0x4E52F0, hero, spell, o_BattleMgr->spec_terr_type);
}

bool _BattleStackEx_::magicMirrorLucky(const int spell)
{
   int effSchoolLevel = getEffSchoolLevel(o_BattleMgr->hero[this->side], SPL_MAGIC_MIRROR);

   return !this->isImmuneTo(spell) && !(o_Spell[spell].flags & SPF_FRIENDLY_HAS_MASS) && (this->side != o_BattleMgr->current_side ||
       spell == SPL_FROST_RING || spell == SPL_FIREBALL || spell == SPL_INFERNO || spell == SPL_METEOR_SHOWER ||
       spell == SPL_DEATH_CLOUD_NEW || spell == SPL_INCINERATION || spell == SPL_BERSERK || spell == SPL_DEATH_RIPPLE ||
       spell == SPL_DESTROY_UNDEAD || spell == SPL_ARMAGEDDON) && this->active_spell_duration[SPL_MAGIC_MIRROR] > 0 &&
       Randint(1, 100) <= o_Spell[SPL_MAGIC_MIRROR].effect[effSchoolLevel];
}

_BattleStackEx_* _BattleStackEx_::getStackToRedirect(const int spell, const bool massCast)
{
   _BattleStackEx_* stackFoe = 0;
   int sideFoe = 1 - this->side;

   if ( stackIndex < (int)stackVector.size() && spell != SPL_DESTROY_UNDEAD && spell != SPL_DEATH_RIPPLE && spell != SPL_ARMAGEDDON )
       stackFoe = (_BattleStackEx_*)&o_BattleMgr->stack[sideFoe][stackVector[stackIndex++]->index_on_side];

   if ( !massCast && !stackFoe )
   {
       int i = 0;
       // Пропускаем погибшие отряды
       while (o_BattleMgr->stack[o_BattleMgr->current_side][i].is_killed) ++i;
       stackFoe = (_BattleStackEx_*)&o_BattleMgr->stack[sideFoe][i];
   }
 
   return stackFoe;
}

float _BattleStackEx_::getFullVulnerability(const int spell)
{
   float magicMirrorChance = this->active_spell_duration[SPL_MAGIC_MIRROR] ?
       o_Spell[SPL_MAGIC_MIRROR].effect[this->active_spells_power[SPL_MAGIC_MIRROR]] * 0.01f : 0;
   return (1 - magicMirrorChance) * this->getMagicVulnerability(spell);
};

int _BattleStackEx_::calcMagicDamage(const int spell, const _Hero_* attHero, const _Hero_* defHero)
{
   int effSchoolLevel = 0;
 
   if ( attHero )
       effSchoolLevel = getEffSchoolLevel(attHero, SPL_MAGIC_MIRROR);
   // Если кастер - нейтралы на Magic Plains
   else if ( o_BattleMgr->spec_terr_type == 1 )
       effSchoolLevel = 3;

   int damage = attHero->power * o_Spell[spell].eff_power + o_Spell[spell].effect[effSchoolLevel];
   return CALL_7(int, __thiscall, 0x5A7BF0, o_BattleMgr, damage, spell, attHero, defHero, this, 0);
}

int __stdcall newTarget(LoHook* h, HookContext* c)
{
   _BattleStackEx_* stack = (_BattleStackEx_*)c->edi;
   int spell = *(int*)(c->ebp + 8);

   c->eax = (int)stack->getStackToRedirect(spell, false);
   redirectedSingleCast = true;

   return EXEC_DEFAULT;
}

float __stdcall calcMagicMirrorResist(HiHook* h, _BattleMgr_* battleMgr, int spell, int side, _BattleStackEx_* stack, int a5, int a6, int a7)
{
   float magicVulnerability = CALL_7(float, __thiscall, h->GetDefaultFunc(), o_BattleMgr, spell, side, stack, a5, a6, a7);
   float magicMirrorChance = stack->active_spell_duration[SPL_MAGIC_MIRROR] ?
       o_Spell[SPL_MAGIC_MIRROR].effect[stack->active_spells_power[SPL_MAGIC_MIRROR]] * 0.01f : 0;
 
   return redirectedSingleCast ? (1 - magicMirrorChance) * magicVulnerability : magicVulnerability;   
}

bool __stdcall massSpellMagicMirror(HiHook *h, int spell, int ssLevel)
{
   return spell == SPL_MAGIC_MIRROR && ssLevel == 3 ? false : CALL_2(bool, __fastcall, h->GetDefaultFunc(), spell, ssLevel);
}

int _BattleStackEx_::getMonsterSpell()
{
   int spell = ID_NONE;

   if ( this )
   {
       switch ( this->creature_id )
       {
       case CID_THUNDERBIRD:
           spell = SPL_THUNDERBOLT;
           break;
       case CID_DRAGON_FLY:
           spell = SPL_WEAKNESS;
           break;
       case CID_UNICORN:
       case CID_WAR_UNICORN:
           spell = SPL_BLIND;
           break;
       case CID_BLACK_KNIGHT:
       case CID_DREAD_KNIGHT:
       case CID_MUMMY:
           spell = SPL_CURSE;
           break;
       }
   }

   return spell;
}

int __stdcall dataInit(LoHook* h, HookContext* c)
{
   int spell;
   vector<_BattleStackEx_*> tmpVector;
 
   if ( h->GetAddress() == 0x5A058B )
       spell = *(int*)(c->ebp + 8);
   else {
       _BattleStackEx_* stack = (_BattleStackEx_*)c->esi;
       spell = stack->getMonsterSpell();
   }

   if ( spell != ID_NONE )
   {
       showMagicMirrorAnim = false;
       showMagicResistAnim = false;
       showSpellAnim = false;
       redirectedSingleCast = false;
       redirectedAreaSpell = false;
       memset(needMagicMirrorAnim, false, 40);
       memset(needMagicResistAnim, false, 40);
       memset(needSpellAnim, false, 40);

       int casterSide = o_BattleMgr->current_side;
       int maxSize = o_BattleMgr->stacks_count[casterSide];
       stackVector.resize(maxSize);
       tmpVector.resize(maxSize);

       int n = 0;
       int k = 0;
       for (int i = 0; i < maxSize; ++i)
       {
           _BattleStackEx_* stack = (_BattleStackEx_*)&o_BattleMgr->stack[casterSide][i];
           if ( stack->approvedTarget(spell) )
           {
               if ( stack->active_spell_duration[SPL_BLIND] || stack->active_spell_duration[SPL_STONE] ||
                   stack->active_spell_duration[SPL_PARALYZE] )
               {
                   // В этот вектор помещаем отряды под Blind/Stone/Paralyze,
                   tmpVector[k++] = stack;
               }
               else
               {
                   // а в этот - все остальные.
                   stackVector[n++] = stack;
               }
           }
       }

       stackVector.resize(n);
       tmpVector.resize(k);
     
       // Ударные заклинания (можете также попробовать вместо условия ниже следующее: o_Spell[spell].flags & 0x200)
       if ( spell >= SPL_MAGIC_ARROW && spell <= SPL_CHAIN_LIGHTNING || spell == SPL_TITANS_LIGHTNING_BOLT )
       {
           _Hero_* attHero = o_BattleMgr->hero[o_BattleMgr->current_side];
           _Hero_* defHero = o_BattleMgr->hero[1 - o_BattleMgr->current_side];

           sort(stackVector.begin(), stackVector.end(), cmpExtraFightValue(spell, attHero, defHero));
           stable_sort(stackVector.begin(), stackVector.end(), cmpDamageFightValue(spell, attHero, defHero));
       }
       // Неударные заклинания
       else
       {
           // Сортируем отряды не под Blind/Stone/Paralyze
           sort(stackVector.begin(), stackVector.end(), cmpFightValue(spell));
           stable_sort(stackVector.begin(), stackVector.end(), cmpSpellDuration(spell));
         
           // Сортируем отряды под Blind/Stone/Paralyze
           sort(tmpVector.begin(), tmpVector.end(), cmpFightValue(spell));
           stable_sort(tmpVector.begin(), tmpVector.end(), cmpSpellDuration(spell));

           // Объединяем отсортированные векторы
           stackVector.insert(stackVector.end(), tmpVector.begin(), tmpVector.end());
       }

       stackIndex = 0;
   }

   return EXEC_DEFAULT;
}

int __stdcall magicMirrorAI(LoHook* h, HookContext* c)
{
   _BattleStackEx_* stack = (_BattleStackEx_*)c->edi;

   if ( stack )
   {
       _Hero_* hero = o_BattleMgr->hero[1 - o_BattleMgr->current_side];
       int spell = *(int*)(c->ebp + 8);
       int effSchoolLevel = getEffSchoolLevel(hero, SPL_MAGIC_MIRROR);

       // Если герой - AI, на отряде висит Magic Mirror и оно сработало, а также оригинальные проверки на Teleport и Sacrifice
       if ( !CALL_2(bool, __thiscall, 0x4CE600, o_GameMgr, o_BattleMgr->owner_id[o_BattleMgr->current_side]) &&
            stack->active_spell_duration[SPL_MAGIC_MIRROR] > 0 && Randint(1, 100) <= o_Spell[SPL_MAGIC_MIRROR].effect[effSchoolLevel] &&
            spell != SPL_TELEPORT && spell != SPL_SACRIFICE )
       {
           // Проигрываем одиночную анимацию
           stack->playAnimation(o_Spell[SPL_MAGIC_MIRROR].animation_ix);

           c->return_address = 0x5A05CC;
           return NO_EXEC_DEFAULT;
       }
   }
 
   return EXEC_DEFAULT;
}

void playSound(const char* fileName)
{
   if (!o_BattleMgr->ShouldNotRenderBattle())
      CALL_1(_Sample_, __fastcall, 0x59A770, fileName);
}

void playMagicResist()
{
   // Проигрываем массовую анимацию и звук резиста
   if ( showMagicResistAnim )
   {
       playSound("MagicRes.wav");
       CALL_4(void, __thiscall, 0x5A6AD0, o_BattleMgr, &needMagicResistAnim, 78, 0);
   }
}

void playMagicMirror()
{
   // Проигрываем массовую анимацию и звук Magic Mirror
   if ( showMagicMirrorAnim )
   {
       playSound(o_Spell[SPL_MAGIC_MIRROR].wav_name);
       CALL_4(void, __thiscall, 0x5A6AD0, o_BattleMgr, &needMagicMirrorAnim, o_Spell[SPL_MAGIC_MIRROR].animation_ix, 0);
   }
}

int __stdcall skipSpellSound(LoHook* h, HookContext* c)
{
   if ( !c->edi ) {
       int spell = *(int*)(c->ebp + 8);
       char switchCase = ((char*)0x5A2B48)[spell - 10];

       // При следующих условиях не проигрываем звук заклинания
       if ( spell == SPL_DEATH_RIPPLE || spell == SPL_DESTROY_UNDEAD ||
           switchCase == 17 || switchCase == 24 || o_BattleMgr->ShouldNotRenderBattle() )
       {
           c->return_address = 0x5A0646;
           return NO_EXEC_DEFAULT;
       }
   }

   return EXEC_DEFAULT;
}

int __stdcall massReflection(LoHook* h, HookContext* c)
{
   bool hitFlag = false;
   _ptr_ addr = h->GetAddress();

   if ( addr == hAddr[A_MASSREFLECTION_HOOK][E_MASS] )
       c->return_address = hAddr[A_MASSANIMATION][E_MASS];
 
   if ( addr == hAddr[A_MASSREFLECTION_HOOK][E_BERSERK] )
       c->return_address = hAddr[A_MASSANIMATION][E_BERSERK];
 
   if ( addr == hAddr[A_MASSREFLECTION_HOOK][E_DEATH_RIPPLE] )
   {
       hitFlag = true;
       c->return_address = hAddr[A_MASSANIMATION][E_DEATH_RIPPLE];
   }

   if ( addr == hAddr[A_MASSREFLECTION_HOOK][E_DESTROY_UNDEAD] )
   {
       c->edi = *(int*)(c->ebp - 0x10);
       hitFlag = true;
       c->return_address = hAddr[A_MASSANIMATION][E_DESTROY_UNDEAD];
   }

   playMagicMirror();
   playMagicResist();
 
   if ( addr == hAddr[A_MASSREFLECTION_HOOK][E_ARMAGEDDON] )
   {
       memcpy(AnimateStack, needSpellAnim, 40);
       return EXEC_DEFAULT;
   }

   // Проигрываем массовую анимацию и звук кастуемого заклинания
   if ( showSpellAnim )
   {
       _Spell_* spell = (_Spell_*)*(int*)(c->ebp - 0x10);
       playSound(spell->wav_name);
       CALL_4(void, __thiscall, 0x5A6AD0, o_BattleMgr, &needSpellAnim, spell->animation_ix, hitFlag);
   }

   return NO_EXEC_DEFAULT;
}

bool isValidHex(const int hex)
{
   return hex >= 0 && hex < 187;
}

int getSpellTargetHex(const int spell)
{
   const int xSize = 17;
             
   vector <pair<double, double>> ones;
   ones.resize(186);
 
   double maxDamageFightValueDelta = 0.0;
   int bestTargetHex = -1;

   _Hero_* attHero = o_BattleMgr->hero[o_BattleMgr->current_side];
   _Hero_* defHero = o_BattleMgr->hero[1 - o_BattleMgr->current_side];

   for (int hex = 1; ; ++hex)
   {
       if ( hex % xSize == 16 ) hex += 2;
       if ( hex > 185 ) break;
   
       int y = hex / xSize;
       int d = y % 2;

       int areaR1[] = {-17 - d, -16 - d, -1, 0, 1, 17 - d, 18 - d};
       int areaFR[] = {-17 - d, -16 - d, -1, 1, 17 - d, 18 - d};
       int areaR2[] = {-35, -34, -33, -18 - d, -17 - d, -16 - d, -15 - d, -2, -1,
               0, 1, 2, 16 - d, 17 - d, 18 - d, 19 - d, 33, 34, 35};
     
       int* area = areaR1;
       int areaSize = sizeof(areaR1) / sizeof(int);
     
       if ( spell == SPL_FROST_RING ) {
           area = areaFR;
           areaSize = sizeof(areaFR) / sizeof(int);
       }

       if ( spell == SPL_INFERNO ) {
           area = areaR2;
           areaSize = sizeof(areaR2) / sizeof(int);
       }
     
       int countUs = 0, countThem = 0;
       double extraFightValueUs = 0, extraFightValueThem = 0;
       double damageFightValueUs = 0, damageFightValueThem = 0;
       for (int i = 0; i < areaSize; ++i)
       {
           int iHex = hex + area[i];
           if ( isValidHex(iHex) )
           {
               _BattleStackEx_* stack = (_BattleStackEx_*)o_BattleMgr->hex[iHex].GetCreature();
               if ( stack )
               {
                   if ( !stack->is_killed && !stack->isImmuneTo(spell) )
                   {
                       // Переходим к следующей области, если хотя бы один отряд в ней находится под действием заклинаний:
                       if ( stack->active_spell_duration[SPL_BLIND] || stack->active_spell_duration[SPL_STONE] ||
                           stack->active_spell_duration[SPL_PARALYZE] )
                       {
                           ones[hex].first = 0;
                           goto skipArea;
                       }
                     
                       // Считаем магический урон, получаемый стеком stack
                       int spellDamage = stack->calcMagicDamage(spell, attHero, defHero);
                     
                       if ( stack->side == o_BattleMgr->current_side ) {
                           ++countThem;
                           damageFightValueThem += stack->damageFightValue(spell, spellDamage);
                           extraFightValueThem += stack->extraFightValue(spell, spellDamage);
                       }
                       else {
                           ++countUs;
                           damageFightValueUs += stack->damageFightValue(spell, spellDamage);
                           extraFightValueUs += stack->extraFightValue(spell, spellDamage);
                       }
                   }
                                 
                   if ( i + 1 < areaSize )
                   {
                       int iHexNext = hex + area[i + 1];
                       if ( isValidHex(iHexNext) && (_BattleStackEx_*)o_BattleMgr->hex[iHexNext].GetCreature() == stack ) ++i;
                   }
               }
           }
       }

       ones[hex].first = damageFightValueThem - damageFightValueUs;
       ones[hex].second = extraFightValueThem - extraFightValueUs;

       if ( countThem + countUs > 0 && damageFightValueThem - damageFightValueUs > maxDamageFightValueDelta )
       {
           maxDamageFightValueDelta = damageFightValueThem - damageFightValueUs;
           bestTargetHex = hex;
       }

       skipArea:;
   }

   for (int i = 1; i < 186; ++i)
       if ( maxDamageFightValueDelta > 0.0 && ones[i].first == maxDamageFightValueDelta )
       {
           if ( ones[i].second > ones[bestTargetHex].second )
               bestTargetHex = i;
       }

   return bestTargetHex;
}

void __stdcall areaReflection(HiHook* h, _BattleMgr_* battleMgr, int hex, int spell, int a3, int a4)
{
   memcpy(AnimateStack, needSpellAnim, 40);
   CALL_5(void, __thiscall, h->GetDefaultFunc(), battleMgr, hex, spell, a3, a4);
   playMagicResist();
   playMagicMirror();

   // Если заклинание отражено
   if ( redirectedAreaSpell )
   {
       int bestHex = getSpellTargetHex(spell);
       if ( bestHex != ID_NONE )
       {
           showMagicMirrorAnim = false;
           showMagicResistAnim = false;
           memset(needMagicMirrorAnim, false, 40);
           memset(needMagicResistAnim, false, 40);

           playSound(o_Spell[spell].wav_name);
           CALL_5(void, __thiscall, h->GetDefaultFunc(), battleMgr, bestHex, spell, a3, a4);
           playMagicResist();
           playMagicMirror();
       }
   }
}

int __stdcall reflectSpell(LoHook* h, HookContext* c)
{
   _Hero_* hero = 0;
   _BattleStackEx_* stack = 0;
   int spell, spellPower, schoolLevel;
   int casterSide = o_BattleMgr->current_side;

   _ptr_ addr = h->GetAddress();

   if ( addr == hAddr[A_REFLECTSPELL_HOOK][E_MASS] )
   {
      stack = (_BattleStackEx_*)c->esi;
      spell = *(int*)(c->ebp + 0xC);
      spellPower = *(int*)(c->ebp + 0x14);
      schoolLevel = *(int*)(c->ebp + 0x10);
      hero = *(_Hero_**)(c->ebp + 8);
      c->return_address = hAddr[A_REFLECTSPELL_SKIP][E_MASS];
   }

   if ( addr == hAddr[A_REFLECTSPELL_HOOK][E_BERSERK] )
   {
      stack = *(_BattleStackEx_**)(c->ebp + 0x14);
      spell = *(int*)(c->ebp + 8);
      spellPower = *(int*)(c->ebp + 0x1C);
      schoolLevel = c->esi;
      hero = *(_Hero_**)(c->ebp - 0x14);
      c->return_address = hAddr[A_REFLECTSPELL_SKIP][E_BERSERK];
   }

   if ( addr == hAddr[A_REFLECTSPELL_HOOK][E_AREA] )
   {
       stack = (_BattleStackEx_*)c->edi;
       spell = c->esi;
       schoolLevel = *(int*)(c->ebp - 0x44);
       c->return_address = hAddr[A_REFLECTSPELL_SKIP][E_AREA];
   }

   if ( addr == hAddr[A_REFLECTSPELL_HOOK][E_DEATH_RIPPLE] )
   {
       stack = (_BattleStackEx_*)c->edi;
       spell = SPL_DEATH_RIPPLE;
       c->return_address = hAddr[A_REFLECTSPELL_SKIP][E_DEATH_RIPPLE];
   }

   if ( addr == hAddr[A_REFLECTSPELL_HOOK][E_DESTROY_UNDEAD] )
   {
       stack = (_BattleStackEx_*)c->edi;
       spell = SPL_DESTROY_UNDEAD;
       c->return_address = hAddr[A_REFLECTSPELL_SKIP][E_DESTROY_UNDEAD];
   }

   if ( addr == hAddr[A_REFLECTSPELL_HOOK][E_ARMAGEDDON] )
   {
       stack = (_BattleStackEx_*)c->esi;
       spell = SPL_ARMAGEDDON;
       c->return_address = hAddr[A_REFLECTSPELL_SKIP][E_ARMAGEDDON];
   }

   // Получаем оригинальный шанс срабатывания заклинания
   int hitChance = c->eax;

   if ( stack )
   {
       bool isSpellAoE = spell == SPL_FROST_RING || spell == SPL_FIREBALL || spell == SPL_INFERNO ||
          spell == SPL_METEOR_SHOWER || spell == SPL_DEATH_CLOUD_NEW || spell == SPL_INCINERATION;
       bool isSpellSpec = spell == SPL_DEATH_RIPPLE || spell == SPL_DESTROY_UNDEAD || spell == SPL_ARMAGEDDON;

       if ( stack->magicMirrorLucky(spell) )
       {
           _BattleStackEx_* stackFoe = stack->getStackToRedirect(spell);

           if ( stackFoe && !redirectedAreaSpell ) {
               showMagicMirrorAnim = true;
               needMagicMirrorAnim[stack->side][stack->index_on_side] = true;
           }
           else {
               showMagicResistAnim = true;
               needMagicResistAnim[stack->side][stack->index_on_side] = true;
           }

           // Не перенаправляем площадные заклинания, отражённые от дружественных отрядов
           if ( isSpellAoE && stack->side != casterSide ) redirectedAreaSpell = true;
         
           if ( isSpellAoE || isSpellSpec ) return NO_EXEC_DEFAULT;

           // Если сработало Magic Mirror, перенаправляем заклинание на вражеский отряд
           if ( stackFoe )
           {
               // Определяем, сработает ли резист против отражённого заклинания
               int hitChanceFoe = stackFoe->getHitChanceFull(spell);

               if ( Randint(1, 100) <= hitChanceFoe )
               {
                   showSpellAnim = true;
                   needSpellAnim[stackFoe->side][stackFoe->index_on_side] = true;
                   CALL_5(void, __thiscall, 0x444610, stackFoe, spell, spellPower, schoolLevel, hero);
               }
               else if ( hitChanceFoe )
               {
                   showMagicResistAnim = true;
                   needMagicResistAnim[stackFoe->side][stackFoe->index_on_side] = true;
               }
           }
       }
       else
       {
           if ( Randint(1, 100) <= hitChance )
           {
               showSpellAnim = true;
               needSpellAnim[stack->side][stack->index_on_side] = true;
               AnimateStack[stack->side][stack->index_on_side] = true;

               if ( isSpellAoE || isSpellSpec )
               {
                   c->return_address = getRA(spell);
                   return NO_EXEC_DEFAULT;
               }
             
               // Если не сработал резист, применяем эффект заклинания
               CALL_5(void, __thiscall, 0x444610, stack, spell, spellPower, schoolLevel, hero);
           }
           else if ( hitChance )
           {
               showMagicResistAnim = true;
               needMagicResistAnim[stack->side][stack->index_on_side] = true;
           }
       }
   }
 
   return NO_EXEC_DEFAULT;
}

int __stdcall mirrorMonsterSpell(LoHook* h, HookContext* c)
{
   int spell;
   _BattleStackEx_* stack = (_BattleStackEx_*)c->edi;

   _ptr_ addrHit, addrSkip;
   switch ( h->GetAddress() )
   {
   case 0x440EE8:
       spell = SPL_THUNDERBOLT;
       addrHit = 0x440F03;
       addrSkip = 0x4412AB;
       break;
   case 0x440372:
       spell = SPL_BLIND;
       addrHit = 0x44038D;
       addrSkip = 0x4402AE;
       break;
   case 0x440467:
       spell = SPL_CURSE;
       addrHit = 0x440482;
       addrSkip = 0x4402AE;
       break;
   }

   if ( stack->magicMirrorLucky(spell) )
   {
       c->return_address = addrSkip;
     
       // Если сработало Magic Mirror, отражаем обратно
       _BattleStackEx_* stackFoe = (_BattleStackEx_*)c->esi;
       if ( stackFoe )
       {
           int hitChanceFoe = stackFoe->getHitChanceResist(spell);
         
           if ( Randint(1, 100) <= hitChanceFoe )
           {
               if ( spell == SPL_THUNDERBOLT )
               {
                   playSound(o_Spell[SPL_MAGIC_MIRROR].wav_name);
                   stack->playAnimation(o_Spell[SPL_MAGIC_MIRROR].animation_ix);
               }
               else
               {
                   targetStack = stack;
                   showMagicMirrorAfterAttack = true;
               }

               c->edi = (int)stackFoe;
               c->return_address = addrHit;
           }
           else if ( hitChanceFoe )
           {
               if ( spell == SPL_THUNDERBOLT )
               {
                   playSound("MagicRes.wav");
                   stackFoe->playAnimation(78);
               }
               else
               {
                   targetStack = stackFoe;
                   showResistAfterAttack = true;
               }
           }
       }
                     
       return NO_EXEC_DEFAULT;
   }
     
   return EXEC_DEFAULT;
}

int __stdcall playAnimForMonster(LoHook* h, HookContext* c)
{
   if ( showResistAfterAttack )
   {
       playSound("MagicRes.wav");
       targetStack->playAnimation(78);
       showResistAfterAttack = false;
   }

   if ( showMagicMirrorAfterAttack )
   {
       playSound(o_Spell[SPL_MAGIC_MIRROR].wav_name);
       targetStack->playAnimation(o_Spell[SPL_MAGIC_MIRROR].animation_ix);
       showMagicMirrorAfterAttack = false;
   }

   return EXEC_DEFAULT;
}

int __stdcall playMagicMirrorSound(LoHook* h, HookContext* c)
{
   playSound(o_Spell[SPL_MAGIC_MIRROR].wav_name);
 
   return EXEC_DEFAULT;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
   static bool plugin_On = false;

   if ( DLL_PROCESS_ATTACH == ul_reason_for_call )
   {
       if ( !plugin_On )
       {
           plugin_On = true;
           _P = GetPatcher();
           _PI = _P->CreateInstance((char*)"HD.Plugin.NewMagicMirrorBeta");

           // Пропускаем оригинальный звук заклинания, чтобы воспроизвезти его позже
           _PI->WriteLoHook(0x5A0634, skipSpellSound);
         
           // Инициализируем данные
           _PI->WriteLoHook(0x441893, dataInit);
           _PI->WriteLoHook(0x441991, dataInit);
           _PI->WriteLoHook(0x5A058B, dataInit);
         
           // Синглкаст
           _PI->WriteLoHook(0x5A05CC, newTarget);
           _PI->WriteLoHook(0x5A058B, magicMirrorAI);
           _PI->WriteHiHook(0x5A83A0, SPLICE_, EXTENDED_, THISCALL_, calcMagicMirrorResist);

           // Масскаст
           for (int i = 0; i < E_SIZE; ++i)
           {
               _PI->WriteLoHook(hAddr[A_REFLECTSPELL_HOOK][i], reflectSpell);
               if ( i == E_AREA )
                   _PI->WriteHiHook(hAddr[A_MASSREFLECTION_HOOK][i], SPLICE_, EXTENDED_, THISCALL_, areaReflection);
               else
                   _PI->WriteLoHook(hAddr[A_MASSREFLECTION_HOOK][i], massReflection);
           }
         
           // Проигрываем звук Magic Mirror при отражении одиночного заклинания
           _PI->WriteLoHook(0x5A059A, playMagicMirrorSound);

           _PI->WriteLoHook(0x4EE1C1, afterInit);
           _PI->WriteHiHook(0x59E360, SPLICE_, EXTENDED_, FASTCALL_, massSpellMagicMirror);

           _PI->WriteLoHook(0x440EE8, mirrorMonsterSpell); // Thunderstrike
           _PI->WriteLoHook(0x440372, mirrorMonsterSpell); // Blind
           _PI->WriteLoHook(0x440467, mirrorMonsterSpell); // Curse
           _PI->WriteLoHook(0x468C99, playAnimForMonster);
       }
   }
   
   return TRUE;
}

NewMagicMirror.zip
(19.53 КБ) Скачиваний: 7

Исправлено отсутствие анимации для Expert Bloodlust.
Исправлено воспроизведение звуков заклинаний во время быстрого боя.
Спасибо Нмеса за баг-репорты.
Последний раз редактировалось AlexSpl 29 апр 2022, 16:13, всего редактировалось 7 раз(а).
Вернуться к началу

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

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

Сообщение AlexSpl » 16 июн 2021, 11:30

Авторы: AlexSpl, Rolex

Название: NewDwellings

Описание: Во внешних жилищах можно нанимать улучшенных существ при наличии улучшенной постройки этих существ хотя бы в одном городе игрока. Найм существ первого уровня платный.

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

Patcher* _P;
PatcherInstance* _PI;

int getCreatureBasicType(const int id)
{
    int basicType = id;
   
    if ( id % 2 && (id < CID_AIR_ELEMENTAL || id == CID_SPRITE || id == CID_MAGIC_ELEMENTAL || id == CID_PHOENIX) )
        basicType = id - 1;
    else
    switch ( id )
    {
    case CID_STORM_ELEMENTAL:
        basicType = CID_AIR_ELEMENTAL;
        break;
    case CID_MAGMA_ELEMENTAL:
        basicType = CID_EARTH_ELEMENTAL;
        break;
    case CID_ENERGY_ELEMENTAL:
        basicType = CID_FIRE_ELEMENTAL;
        break;
    case CID_ICE_ELEMENTAL:
        basicType = CID_WATER_ELEMENTAL;
        break;
    }

    return basicType;
}

int getCreatureUpgradeType(const int id)
{
    return CALL_1(int, __fastcall, 0x47AAD0, id);
}

_Dwelling_* GetDwelling(const int id)
{
    return (_Dwelling_ *)(o_GameMgr->Field<int>(0x4E39C) + 92 * id);
}

_Dwelling_* getDwelling(const _Hero_* hero)
{
    _Dwelling_* dwelling = 0;
   
    if ( hero )
    {
        _MapItem_* item = o_GameMgr->Map.GetItem(hero->x, hero->y, hero->z);
        if ( item && (item->object_type == 0x11 || item->object_type == 0x14) ) dwelling = GetDwelling(item->setup);
    }

    return dwelling;
}

bool playerHasUpgDwelling(const int id)
{
    bool hasUpgDwelling = false;

    for (int i = 0; i < o_ActivePlayer->towns_count; ++i)
    {
        _Town_* town = o_GameMgr->GetTown(o_ActivePlayer->towns_ids[i]);
        if ( town && o_pCreatureInfo[id].town == town->type &&
            town->IsBuildingBuilt(37 + o_pCreatureInfo[id].level, 1) )
        {
            hasUpgDwelling = true;
            break;
        }
    }

    return hasUpgDwelling;
}

void __stdcall setupDwelling(HiHook* h, _AdvMgr_* advMgr, _Hero_* hero, _MapItem_* item, int a4, int a5)
{
    _Dwelling_* dwelling = GetDwelling(item->setup);
           
    if ( dwelling->type == 0x11 )
    {
        int upgCreatureId = getCreatureUpgradeType(dwelling->creature_types[0]);
       
        if ( upgCreatureId != ID_NONE && playerHasUpgDwelling(upgCreatureId) )
        {
            if ( o_ActivePlayer->IsHuman() )
            {
                dwelling->creature_types[1] = dwelling->creature_types[0];
                dwelling->creature_counts[1] = dwelling->creature_counts[0];
            }

            dwelling->creature_types[0] = upgCreatureId;
           
            if ( dwelling->defenders.count[0] > 0 )
                dwelling->defenders.type[0] = upgCreatureId;
        }
    }

    CALL_5(void, __thiscall, h->GetDefaultFunc(), advMgr, hero, item, a4, a5);

    if ( dwelling->type == 0x11 )
    {   
        dwelling->creature_types[0] = getCreatureBasicType(dwelling->creature_types[0]);
        if ( dwelling->creature_types[1] != ID_NONE)
            dwelling->creature_counts[0] = min(dwelling->creature_counts[0], dwelling->creature_counts[1]);
        dwelling->creature_types[1] = ID_NONE;
        dwelling->creature_counts[1] = 0;
        if ( dwelling->defenders.count[0] > 0 )
            dwelling->defenders.type[0] = dwelling->creature_types[0];
    }
}

int __stdcall afterHiring(LoHook* h, HookContext* c)
{
    _Dwelling_* dwelling = 0;
   
    if ( o_ActivePlayer->selected_hero_id != ID_NONE )
        dwelling = getDwelling(&o_GameMgr->hero[o_ActivePlayer->selected_hero_id]);
   
    if ( dwelling && dwelling->type == 0x11 )
    {
        c->return_address = 0x55121B;
        return NO_EXEC_DEFAULT;
    }

    return EXEC_DEFAULT;
}

int __stdcall skipMessages(LoHook* h, HookContext* c)
{
    if ( _P->VarGetValue("HD.UI.AdvMgr.SkipMapMsgs", 1) )
    {
        c->return_address = 0x4A1917;
        return NO_EXEC_DEFAULT;
    }

    return EXEC_DEFAULT;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    static bool plugin_On = false;

    if ( DLL_PROCESS_ATTACH == ul_reason_for_call )
    {
        if ( !plugin_On )
        {
            plugin_On = true;
            _P = GetPatcher();
            _PI = _P->CreateInstance((char*)"HD.Plugin.UpgradedDwellings");

            // Пропускаем сообщения при HD.UI.AdvMgr.SkipMapMsgs = 1
            _PI->WriteLoHook(0x4A17BD, skipMessages);
            // Устанавливаем тип существ и защитников при посещении двеллинга
            _PI->WriteHiHook(0x4A1520, SPLICE_, EXTENDED_, THISCALL_, setupDwelling);
            // Закрываем диалог после нажатия на кнопку покупки
            _PI->WriteLoHook(0x5510B1, afterHiring);
                       
            // Убираем бесплатное присоединение существ из двеллингов 1-го уровня
            _PI->WriteHexPatch(0x4A197B, "90 90");
            _PI->WriteHexPatch(0x4AB812, "90 90");
           
            // AI
            // Убираем бесплатное присоединение существ из двеллингов 1-го уровня
            _PI->WriteHexPatch(0x42CC06, "90 90 90");
        }
    }

    return TRUE;
}

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

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

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

Сообщение AlexSpl » 30 июл 2021, 14:58

Автор: AlexSpl

Название: NewEagleEye

Описание: С вероятностью 40/60/80% на Базовом/Продвинутом/Экспертном уровне Орлиного глаза (Eagle Eye) соответственно герой может выучить заклинание, направленное вражеским героем. Базовый уровень Орлиного глаза позволяет изучать заклинания 1-3 уровня, Продвинутый - 1-4 уровня, Экспертный - 1-5 уровня. Заклинания изучаются как только герой с Орлиным глазом получает ход.

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

Patcher* _P;
PatcherInstance* _PI;

struct PicStruc {
    int type;       // тип картинки (9 - заклинание)
    int id;         // ID картинки
};

// кастомный _List_
struct List {
    _ptr_ Creation;
    PicStruc* Data;
    PicStruc* EndData;
    _ptr_ EndMem;
};

int captionAddr;
int n = 0;
int spells[70];
_Hero_* heroToLearn = 0;

int showSpellDlg(_Hero_* hero, int spells[], int nPics)
{
    if (!nPics) return 0;

    char fileName[MAX_PATH];
    sprintf(fileName, "pickup%02d.82M", Randint(1, 7));
    CALL_1(_Sample_, __fastcall, 0x59A770, fileName);

    List picList;

    // Динамический массив картинок
    // Первый и последний элемент резервируем для полей Creation и EndData/EndMem соответственно
    PicStruc* pic = new PicStruc[nPics + 2];

    for (int i = 0; i < nPics; ++i)
    {
        pic[i + 1].type = 9;
        pic[i + 1].id = spells[i];
    }

    string spellsStr = "";
    if (nPics > 1)
    {
        for (int i = 0; i < nPics - 2; ++i)
        {
            spellsStr.append(o_Spell[spells[i]].name);
            spellsStr.append(", ");
        }

        spellsStr.append(o_Spell[spells[nPics - 2]].name);
        spellsStr.append(o_GENRLTXT_TXT->GetString(238));
        spellsStr.append(o_Spell[spells[nPics - 1]].name);
    }
    else
        spellsStr.append(o_Spell[spells[0]].name);
   
    spellsStr.append(".");

    picList.Creation = (_ptr_)pic + 4;
    picList.Data = pic + 1; // Адрес первого элемента в списке
    picList.EndData = picList.Data + nPics; // Адрес следующего за последним элементом в списке байта
    picList.EndMem = (_ptr_)picList.EndData;

    sprintf(o_TextBuffer, o_GENRLTXT_TXT->GetString(222), hero->name, spellsStr.c_str());
    CALL_5(unsigned int, __fastcall, 0x4F7D20, o_TextBuffer, &picList, -1, -1, 0);

    delete[] pic;

    return 0;
}

int __stdcall learnSpell(LoHook* h, HookContext* c)
{
    _Hero_* hero = o_BattleMgr->hero[1 - o_BattleMgr->current_side];

    if (hero)
    {
        int spell = *(int*)(c->ebp + 8);

        if (spell != ID_NONE && !hero->spell[spell] && hero->doll_art[AS_SPELL_BOOK].id == AID_SPELL_BOOK &&
            o_Spell[spell].level <= hero->second_skill[HSS_WISDOM] + 2 && o_Spell[spell].level <= hero->second_skill[HSS_EAGLE_EYE] + 2)
        {
            int dice = Randint(1, 100);
            int eagleEyeProb = (int)(CALL_1(float, __thiscall, 0x4E4690, hero) * 100.0);
            if (dice <= eagleEyeProb)
            {
                spells[n++] = spell;
                hero->spell[spell] = true;
                hero->spell_level[spell] = true;
            }
        }

        heroToLearn = hero;
    }

    c->return_address = 0x5A02D9;
    return NO_EXEC_DEFAULT;
}

void showSpells()
{
    _Hero_* hero = o_BattleMgr->hero[o_BattleMgr->current_side];

    // Диалог показываем только для игрока-человека:
    // в хотсите - диалог для обоих героев, в сетевой игре - только для своего героя
    if (hero && hero == heroToLearn)
    {
        int id = o_GameMgr->GetMeID();
        if (!o_NetworkGame || hero->owner_id == id)
        {
            o_ActivePlayerID = hero->owner_id;
            showSpellDlg(hero, spells, n);
            o_ActivePlayerID = id;
        }

        n = 0;
        heroToLearn = 0;
    }
}

int __stdcall notifyPlayer(LoHook* h, HookContext* c)
{
    showSpells();

    return EXEC_DEFAULT;
}

int __stdcall notifyPlayerAfterBattle(LoHook* h, HookContext* c)
{
    if (heroToLearn && heroToLearn->owner_id == o_GameMgr->GetMeID())
        showSpellDlg(o_BattleMgr->hero[1 - o_BattleMgr->current_side], spells, n);

    return EXEC_DEFAULT;
}

// Заголовок в каждом диалоге
int __stdcall saveCaption(LoHook* h, HookContext* c)
{
    captionAddr = c->ecx;
    return EXEC_DEFAULT;
}

int __stdcall captionFix(LoHook* h, HookContext* c)
{
    *(int*)(c->ebp - 0x14) = captionAddr;
    return EXEC_DEFAULT;
}

int __stdcall zeroSpells(LoHook* h, HookContext* c)
{
    n = 0;
    heroToLearn = 0;

    return EXEC_DEFAULT;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    static bool plugin_On = false;

    if (DLL_PROCESS_ATTACH == ul_reason_for_call)
    {
        if (!plugin_On)
        {
            plugin_On = true;
            _P = GetPatcher();
            _PI = _P->CreateInstance((char*)"HD.Plugin.NewEagleEye");

            // Меняем коэффициенты
            float eagleEyeCoefs[] = { 0.00f, 0.40f, 0.60f, 0.80f };

            _PI->WriteDword(0x63EA2C, (int&)eagleEyeCoefs[1]);
            _PI->WriteDword(0x63EA30, (int&)eagleEyeCoefs[2]);
            _PI->WriteDword(0x63EA34, (int&)eagleEyeCoefs[3]);

            // Убираем оригинальный эффект
            _PI->WriteHexPatch(0x469C23, (char*)"EB");
            _PI->WriteHexPatch(0x476996, (char*)"E9 DD 01 00 00");

            // Пробуем учить заклинания
            _PI->WriteLoHook(0x5A0262, learnSpell);
           
            // Фиксим заголовок диалога
            _PI->WriteLoHook(0x4F7D49, saveCaption);
            _PI->WriteLoHook(0x4F7D54, captionFix);

            // Показываем выученные заклинания
            _PI->WriteLoHook(0x477C00, notifyPlayer);
            _PI->WriteLoHook(0x46FE20, notifyPlayerAfterBattle);

            _PI->WriteLoHook(0x4AD160, zeroSpells);
        }
    }

    return TRUE;
}

NewEagleEye.zip
(4.94 КБ) Скачиваний: 122

* * *
 Версия, совместимая с плагином NewSpells
NewEagleEye_NewSpellsCompatible.zip
Исходный код внутри архива
(7.11 КБ) Скачиваний: 80
Вернуться к началу

offlineliziwen  
имя: ziwen
 
Сообщения: 4
Зарегистрирован: 14 окт 2021, 07:30
Пол: Мужчина
Поблагодарили: 0 раз.

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

Сообщение liziwen » 02 ноя 2021, 14:19

Автор: liziwen
Имя: PerspectiveEye
Описание: во время битвы красная команда может видеть количество монстров в дикой природе во время ожидания, но синяя команда не может видеть его во время ожидания. Это несправедливо. Этот плагин решает эту проблему.
PS: Я китаец, я не понимаю по-русски и у меня машинный перевод, поэтому выражение может быть неточным.
Код:
Код: Выделить всё
#include "pch.h"
#include "patcher_x86.hpp"

Patcher* _P;
PatcherInstance* _PI;
int eax=0;

int __stdcall skipLoHook(LoHook* h, HookContext* c)
{
     eax = c->eax;
    return EXEC_DEFAULT;
}

int __stdcall recoveryLoHook(LoHook* h, HookContext* c)
{
    if (eax != 0)
    {
        int ecx = 0x9C;
        int temp = eax;
        do
        {
            *(int*)(eax + 0x24) = 0xFFFFFFFF;
            eax = eax + 0x492;
            --ecx;
        } while (ecx);
        eax = temp;
    }
    return EXEC_DEFAULT;
}


BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    static bool plugin_On = false;
 
    if (DLL_PROCESS_ATTACH == ul_reason_for_call)
    {
        if (!plugin_On)
        {
            plugin_On = true;
            _P = GetPatcher();
            _PI = _P->CreateInstance((char*)"HD.Plugin.PerspectiveEye");
            _PI->WriteHexPatch(0x4C7DB0, "90 90 90");
            _PI->WriteLoHook(0x4C7D95, skipLoHook);
            _PI->WriteLoHook(0x450AA0, recoveryLoHook);

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

offlineliziwen  
имя: ziwen
 
Сообщения: 4
Зарегистрирован: 14 окт 2021, 07:30
Пол: Мужчина
Поблагодарили: 0 раз.

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

Сообщение liziwen » 02 ноя 2021, 14:42

К сожалению, по причинам, связанным с сетью, вложение не было загружено.
Вернуться к началу

Пред.След.

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

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

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