Объявления

Друзья, если не получается зарегистрироваться, напишите на почту vdv_forever@bk.ru.
Я оторву свою задницу от всех дел и обязательно Вас активирую! :smile10:
Добро пожаловать на геройский форум! :smile25:

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

Герои Меча и Магии III: Возрождение Эрафии, Герои Меча и Магии III Дыхание Смерти, Герои Меча и Магии III Клинок Армагеддона, Герои Меча и Магии III Хроники Героев
offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 2185 раз.

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

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

Цитата:
Но с Randit() будут повторы.

Повторы будут с HD+. Randint() здесь ни при чём.
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 2185 раз.

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

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

Исправил палатку для игры по сети. Потестил, лечит синхронно у обоих игроков.

Код: Выделить всё
#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 КБ) Скачиваний: 129
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 2185 раз.

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

Сообщение AlexSpl » 17 апр 2021, 00:09

2Rolex: Плохая была идея отражать магию существ на них же самих. Сначала не повезло с Dragonflies (у них Dispel), потом оказалось, что нежити не страшно Проклятие, а теперь я подумал, что и для Единорогов отражение назад малоэффективно: они получают ответку, которая снимает Ослепление :smile1:

Запостил плагин. Может, народ подключится к тестированию. Как минимум, для шоу плагин отлично подходит, особенно эффектно выглядит отражение заклинаний, наносящих урон по площади.
Вернуться к началу

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

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

Сообщение Rolex » 17 апр 2021, 12:26

AlexSpl писал(а):

потом оказалось, что нежити не страшно Проклятие

Ну а если со Сферой узязвимости?

AlexSpl писал(а):

а теперь я подумал, что и для Единорогов отражение назад малоэффективно: они получают ответку, которая снимает Ослепление :smile1:

На самом деле я проверял, после Ослепления ответка не происходит. Так что здесь все ок.

AlexSpl писал(а):

Запостил плагин. Может, народ подключится к тестированию. Как минимум, для шоу плагин отлично подходит, особенно эффектно выглядит отражение заклинаний, наносящих урон по площади.

Напишите в ЛЧ Armageddets пускай как раз потестит его на стабильность в кругу своих игроков.

***************************************************************************

Чтобы все уже по человечески было, по Палатке добавьте следующий код после HealingInfo:

Код: Выделить всё
struct MonBattleDesc
{
   char* nameSingle;
   char* namePlural;
   char* desc;
} monBattleDesc[150];

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

SecSkillDesc FirstAidDesc =
{
   "Первая помощь",
   "{Первая помощь Базового уровня}\n\nВосстанавливает 100 единиц здоровья у всех отрядов в армии героя.",
   "{Первая помощь Продвинутого уровня}\n\nВосстанавливает 200 единиц здоровья у всех отрядов в армии героя, а также здоровье Тележки, Баллисты и Катапульты.",
   "{Первая помощь Экспертного уровня}\n\nВосстанавливает 300 единиц здоровья у всех отрядов и всех орудий в армии героя включая здоровье самой Палатки."
};

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;

   monBattleDesc[CID_FIRST_AID_TENT].desc = "Лечит один из отрядов героя (50 ед. урона без навыка Первая помощь).";
   *(MonBattleDesc*)(0x6703CC + 116 * CID_FIRST_AID_TENT) = monBattleDesc[CID_FIRST_AID_TENT];

   *(SecSkillDesc*)(*(int*)0x67DCF0 + 27 * 16) = FirstAidDesc;

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

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 2185 раз.

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

Сообщение AlexSpl » 17 апр 2021, 12:55

Цитата:
Чтобы все уже по человечески было, по Палатке добавьте следующий код после HealingInfo:

Я думаю, хорошая идея отказаться от практики размещения описаний в коде самого плагина. Да, для двух языков ещё можно, но есть и другие. Поэтому нужно переделать все плагины так, чтобы они читали описание из сопровождающего их текстовика. А текст в нём уже легко перевести на любой нужный язык. Для гибкости можно читать из текстовика также и изменяемые параметры. Короче, идея такая: всё, что мы берём из геройских текстовиков и меняем, должно читаться плагином, например, из сопровождающего ini-файла.
Вернуться к началу

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

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

Сообщение Rolex » 17 апр 2021, 13:06

AlexSpl писал(а):

Я думаю, хорошая идея отказаться от практики размещения описаний в коде самого плагина. Да, для двух языков ещё можно, но есть и другие. Поэтому нужно переделать все плагины так, чтобы они читали описание из сопровождающего их текстовика. А текст в нём уже легко перевести на любой нужный язык. Для гибкости можно читать из текстовика также и изменяемые параметры. Короче, идея такая: всё, что мы берём из геройских текстовиков и меняем, должно читаться плагином, например, из сопровождающего ini-файла.

Как по мне, так это больше актуально для многоязычных плагинов вроде SoD_SP. Для плагинов где используется описание на 1-2 языках удобней, по-моему, размещать его как раз в коде самого плагина. То есть если описание только на рус или рус + англ, то размещаем в коде самого плагина. Если же более 2-х (украинский, польский, французкий и тд) то тогда, конечно, лучше использовать ini-файл.
Вернуться к началу

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

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

Сообщение Rolex » 17 апр 2021, 13:44

Убрал #define _CRT_RAND_S и функцию showDebugInfo и заменил Randint(1, 100) на Mersenne Twister.

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

using namespace std;

Patcher* _P;
PatcherInstance* _PI;

random_device rd;
mt19937 gen(rd());
uniform_int_distribution<int> uid(1, 100);

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

_ptr_ getRA(const int spell)
{
   _ptr_ retAddr;

   switch (spell)
   {
   case SPL_FROST_RING: case SPL_FIREBALL: case SPL_INFERNO: case SPL_METEOR_SHOWER:
      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) &&
      !this->active_spell_duration[SPL_BLIND] && !this->active_spell_duration[SPL_STONE] && !this->active_spell_duration[SPL_PARALYZE];
}

bool _BattleStackEx_::magicMirrorLucky(const int spell)
{
   int effSchoolLevel = this->active_spells_power[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_BERSERK || spell == SPL_DEATH_RIPPLE || spell == SPL_DESTROY_UNDEAD || spell == SPL_ARMAGEDDON) &&
      this->active_spell_duration[SPL_MAGIC_MIRROR] > 0 && uid(gen) <= 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 = CALL_3(int, __thiscall, 0x4E52F0, attHero, spell, o_BattleMgr->spec_terr_type);
   // Если кастер - нейтралы на 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;

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

      int n = 0;
      for (int i = 0; i < maxSize; ++i)
      {
         _BattleStackEx_* stack = (_BattleStackEx_*)&o_BattleMgr->stack[casterSide][i];
         if (stack->approvedTarget(spell)) stackVector[n++] = stack;
      }

      stackVector.resize(n);

      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
      {
         sort(stackVector.begin(), stackVector.end(), cmpFightValue(spell));
         stable_sort(stackVector.begin(), stackVector.end(), cmpSpellDuration(spell));
      }

      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 = stack->active_spells_power[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 && uid(gen) <= 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)
{
   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;
      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 (uid(gen) <= 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 (uid(gen) <= hitChance)
         {
            showSpellAnim = true;
            needSpellAnim[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 (uid(gen) <= 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;
}


AlexSpl, потестируйте мою dll-ку с Вихрем Мерсенна. Подходит ли он нам в данном случае по seed? Прикрепил dll-ку.
Вложения
NewMagicMirror.rar
(16.95 КБ) Скачиваний: 136
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 2185 раз.

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

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

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

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

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

Сообщение Rolex » 17 апр 2021, 13:57

Чем это плохо, вероятность ведь одна же?
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 2185 раз.

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

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

Нет. Это критично. Например, Slow у одного игрока отражается на один отряд, а у другого этот же отряд резистит. Всё. Дальше играть нельзя, т.к. у игроков ходят разные отряды.
Вернуться к началу

Пред.След.

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

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

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