Объявления

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

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

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

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

Сообщение Rolex » 22 мар 2021, 12:12

AlexSpl писал(а):

Осталось написать грамотные алгоритмы выбора цели (в том числе, эпицентра отражённого AoE закла*), оптимизировать код и исправить возможные баги.

Тогда за вами алгоритм выбора цели для площадных заклов, а за мной - для всех остальных. :smile2:

AlexSpl писал(а):

Нужно написать функцию, которая будет проходить по всем гексам поля, и вычислять кол-во вражеских отрядов в нужном радиусе от текущего гекса.

Вот грамотная реализация этой функции, пожалуй, и будет самой сложной задачей в этом плагине.
Вернуться к началу

offlineArmageddets  
Новичок
Новичок
 
Сообщения: 21
Зарегистрирован: 31 окт 2018, 15:05
Пол: Не указан
Поблагодарили: 2 раз.

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

Сообщение Armageddets » 22 мар 2021, 12:12

Да, играли на последней версии по ссылке.
Вот краш лог:
https://dropmefiles.com/0uQ1S
Вот видео:
https://youtu.be/K7Z7xn7_VPI
Вернуться к началу

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 » 22 мар 2021, 12:40

Посмотрел. Попытка прочитать потерянное здоровье несуществующего отряда. Ещё бы сейв перед финалкой...

Давайте так попробуем:

Код: Выделить всё
#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
{
    int stackID;
    int removedDamage;
} healingInfo[21 + 1];

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

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

    if ( hero && hero->second_skill[HSS_FIRST_AID] )
    {
        int n = 0;
        bool needHealing = false;
       
        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].stackID = i;
                    ++n;

                    // Даём алгоритму игры валидную цель для хила
                    *(int*)(c->ebp - 8) = i;
                }
            }
        }
   
        healingInfo[n].stackID = ID_NONE;

        c->return_address = needHealing ? 0x4738C3 : 0x4738B3;
        return NO_EXEC_DEFAULT;
    }

    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] )
    {
        int i = 0;
        while ( healingInfo[i].stackID != ID_NONE )
        {
            _BattleStack_* stack = &o_BattleMgr->stack[o_BattleMgr->current_side][healingInfo[i].stackID];
           
            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;
            }
            ++i;
        }

        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);
       
        // Пишем лог и проигрываем анимацию для всех вылеченных отрядов
        int i = 0;
        while ( healingInfo[i].stackID != ID_NONE )
        {
            _BattleStack_* stack = &o_BattleMgr->stack[o_BattleMgr->current_side][healingInfo[i].stackID];

            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].stackID] = true;
            ++i;
        }

        // Проигрываем массовую анимацию
        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, findTargetsToHeal);

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

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

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

    return TRUE;
}

FirstAidTent.zip
(4.16 КБ) Скачиваний: 124

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

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

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

Сообщение Rolex » 22 мар 2021, 12:48

Только при выборе цели надо бы еще учитывать иммунитеты целевых отрядов к определенным заклам:

Армагеддон: Черные драконы, Золотые драконы, Жар-птицы, Фениксы, Элементали магии, Элементали огня, Элементалей энергии, Ифриты, Султаны Ифритов.
Метеоритный дождь: Черные драконы, Золотые драконы, Элементали магии, Элементали воздуха, Элементалей шторма.
Инферно: Черные драконы, Золотые драконы, Элементали магии, Жар-птицы, Фениксы, Элементали огня, Элементалей энергии, Ифриты, Султаны Ифритов.
Берсерк: Черные драконы, Золотые драконы, Элементали магии, Каменные, Железные, Золотые, Алмазные големы, ВСЯ НЕЖИТЬ (+ нейтралы Мумии), ВСЕ ЭЛЕМЕНТАЛИ (+ Жар-птицы и Фениксы, но без Пикси/Фей), все боевые машины.
Огненный шар: Черные драконы, Золотые драконы, Лазурные драконы, Красные драконы, Зеленые драконы, Элементали магии, Жар-птицы, Фениксы, Элементали огня, Элементали энергии, Ифриты, Султаны ифритов.
Кольцо холода: Черные драконы, Золотые драконы, Лазурные драконы, Красные драконы, Зеленые драконы, Элементали магии, Элементали воды, Элементали льда.

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

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

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

Сообщение Rolex » 23 мар 2021, 13:14

AlexSpl писал(а):

Через o_BattleMgr, конечно :smile1: Некоторые вещи можно получить всегда и везде.

Кстати, есть ещё такое поле у _BattleStack_: int active_spells_power[81]. Попробуйте через него тоже. Можно сравнить результаты потом.

Мне кажется, так даже быстрее: stack->active_spells_power[SPL_ANTI_MAGIC].

Стоп. Вам же уровень развития школы магии нужен :smile14: Тогда только через CALL().

:!: Или всё-таки можно? Длительность у нас в active_spell_duration, поэтому в active_spells_power должен быть уровень школы или величина от него зависящая. Потестируйте как-нибудь. :!:

Да, можно. Все верно, в stack->active_spells_power[spell] у нас уровень развития школы магии. Работает аналогично CALL_3(int, __thiscall, 0x4E52F0, o_BattleMgr->hero[stack->side], SPL_ANTI_MAGIC, o_BattleMgr->spec_terr_type). Посколько он проще и понятней, то его и оставил.

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

Чтобы не дублировать код проверки в dataInit() и newTarget(), была написана еще одна функция самой проверки checkingTheSelectedTarget(). Она проверяет иммунитеты всех монстров к магии или определенным заклам. Если не исключать из цели существ у которых есть иммунитет к определенным заклинаниям, то эффективность Magic Mirror существенно падает.

Допустим, если отразило массовую Печаль два вражеских отряда, а у нас Титаны, Наги и Джинны, то получается, что первое отражение примет на себя Титан, но поскольку у него иммунитет, то этот закл на него попросту не наложится. В итоге мы потеряем одно отражение, второе пойдет на Наг, а Джинны останутся без наложения. Функция checkingTheSelectedTarget учитывает все абилки существ и в данном случае исключит Титинов из списка целей и направит закл на Наг и Джиннов.

Также реализована проверка наложенной Антимагии. Если уровень направляемого закла выше чем тот уровень, который может отразить существо с наложенной Антимагией (проверяется уровень), то мы ложим этого монстра в массив целей, иначе - игнорим его. Для Забывчивости проверется еще может ли проверяемое существо стрелять и неравно ли кол-во оставшихся выстрелов нулю.

По алгоритму идея следующая:
Заводим два глобальных двойных интовых массива, вернее два вектора pair - combatPowerOfStacks и combatPowerOfStacksIsSpell. Если отряд прошел проверку через checkingTheSelectedTarget() для цели, то проверяем есть ли направляемый закл на этом отряде (через active_spell_duration), если нет, то вычисляем и ложим в массив combatPowerOfStacks[count].first боевую мощь этого отряда, а в combatPowerOfStacks[count].second его индекс. Иначе, если на текущий отряд уже наложен направляемый закл, то кладем силу этого отряда в массив combatPowerOfStacksIsSpell[countIsSpell].first, а его индекс в combatPowerOfStacksIsSpell[countIsSpell].second. Чтобы не было сюрпризов после сортировки ресайзим оба массива по кол-ву элементов, которые в них зашли (count и countIsSpell).

После сортим оба массива по убыванию силы отрядов с сохранением индексов. Далее проходимся по второму массиву с отрядами на которых уже имеется направляемый закл и переприсваиваем combatPowerOfStacksIsSpell[i].first вместо силы отрядов длительность в кол-ве раундов по направляемому заклу, при этом индексы не трогаем. После чего сортим полученный массив по убыванию оставшихся раундов действия наложенного закла. Здесь правильней было бы сортить именно в порядке возрастания кол-ва раундов. Но чтобы не писать еще один компаратор я использовал уже имеющийся, при этом в основной массив все попадет в нужном порядке.

И, наконец, объединяем оба массива, вернее кладет второй в первый с конца, чтобы массив с отрядами с наложенными заклами шел после массива отрядов без заклов в порядке возрастания оставшихся раундов.

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

Если направляемый закл уже наложен на часть более сильных отрядов, то в приоритете будут те отряды на которые направляемый спелл еще не был наложен. Если таковых нет, то есть остались только иммуные монстры и монстры на которых уже наложен направляемый спелл, то в приоритете будут уже не самые сильные отряды, а отряды с найменшим кол-вом оставшихся раундов действия заклинания. При этом если два или более отряда имееют одинаковое кол-во оставшихся раундов действия заклинания, то здесь уже приоритет будет отдаваться более сильным отрядам.

Надо бы в наш плагин добавить следующие функции:

1) Хук для изменения оригинального описания на то, что делает плагин. Плюс вероятность отражения. 50% для всех отрядов на Эксперте, это именно то, что нужно. Если будет больше, будет не интересно, большинство игроков просто не будут рисковать, а будут снимать его диспелом. А так 50 на 50, очень интересно будет на Эксперте, будут рисковать. А вдруг пройдет на нужные отряды (а вдруг нет?).

Код: Выделить всё
struct SpellDesc
{
   char* DefaultDesc;
   char* BasicDesc;
   char* AdvancedDesc;
   char* ExpertDesc;
};

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

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 = NewMagicMirror;

   return EXEC_DEFAULT;
}
...
_PI->WriteLoHook(0x4EE1C1, afterInit);


2) Хайхук, который делает Magic Mirror на Эксперте массовым спеллом как для человека, так и для AI.

Код: Выделить всё
char __stdcall massSpellMagicMirror(HiHook *h, int spell, int ssLevel)
{
   if (spell == SPL_MAGIC_MIRROR && ssLevel == 3) return FALSE; else
      return CALL_2(char, __fastcall, h->GetDefaultFunc(), spell, ssLevel);
}
...
_PI->WriteHiHook(0x59E360, SPLICE_, EXTENDED_, FASTCALL_, massSpellMagicMirror);


3) Доработанная функция выбора цели для отраженного одиночного заклинания.

Код: Выделить всё
int __stdcall newTarget(LoHook* h, HookContext* c)
{
   int pos = 0;
   int minDuration = INT_MAX;

   int count = 0;

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

   combatPowerOfStacks.resize(o_BattleMgr->stacks_count[o_BattleMgr->current_side]);

   for (int i = 0; i < combatPowerOfStacks.size(); ++i)
   {
      _BattleStack_* stack = &o_BattleMgr->stack[o_BattleMgr->current_side][i];

      if (checkingTheSelectedTarget(stack, spell))
      {
         combatPowerOfStacks[count].first = o_pCreatureInfo[stack->creature_id].fight_value * stack->count_current;
         combatPowerOfStacks[count].second = i;
         count++;
      }      
   }

   combatPowerOfStacks.resize(count);

   sort(combatPowerOfStacks.begin(), combatPowerOfStacks.end(), my_cmp);

   for (int i = 0; i < count; ++i)
   {
      _BattleStack_* stack = &o_BattleMgr->stack[o_BattleMgr->current_side][combatPowerOfStacks[i].second];
      
      if (stack->active_spell_duration[spell] == 0)
      {
         pos = combatPowerOfStacks[i].second;
         break;
      } else
      if (stack->active_spell_duration[spell] < minDuration)
      {         
         minDuration = stack->active_spell_duration[spell];
         pos = combatPowerOfStacks[i].second;         
      }
   }
   
   c->eax = (int)&o_BattleMgr->stack[o_BattleMgr->current_side][pos];
   
   return EXEC_DEFAULT;
}
...
_PI->WriteLoHook(0x5A05CC, newTarget);


4) Ну и самое главное - функция выбора целей при масскасте (dataInit) с функцией проверки отряда (checkingTheSelectedTarget) с обновленной функцией получения стека для перенаправления (getEnemyStackToRedirectTo) и компаратором (my_cmp).

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

vector <pair<int, int> > combatPowerOfStacks;
vector <pair<int, int> > combatPowerOfStacksIsSpell;
int stackIndex;
...
bool my_cmp(const pair<int, int>& a, const pair<int, int>& b)
{
   return a.first >= b.first;
}

bool checkingTheSelectedTarget(_BattleStack_* stack, int spell)
{
   // int effSchoolLevel = CALL_3(int, __thiscall, 0x4E52F0, o_BattleMgr->hero[stack->side], SPL_ANTI_MAGIC, o_BattleMgr->spec_terr_type);
   int effSchoolLevel = stack->active_spells_power[SPL_ANTI_MAGIC];
   if (effSchoolLevel == 0) effSchoolLevel = 1;

   bool isSubjectToBeAttack = true;

   if (spell == SPL_MAGIC_ARROW || spell == SPL_MISFORTUNE)
   {
      int monsterID[] = { 52, 53, 114, 129, 130, 131 };

      for (int i = 0; i < 6; ++i)
         if (stack->creature_id == monsterID[i])
         {
            isSubjectToBeAttack = false;
            break;
         }
   }
   else
      if (spell == SPL_ICE_BOLT)
      {
         if (stack->creature_id == 115 || stack->creature_id == 123)
            isSubjectToBeAttack = false;
      }
      else
         if (spell == SPL_FORGETFULNESS)
         {
            int monsterID[] = { 41, 64, 65, 123, 127 };

            for (int i = 0; i < 5; ++i)
               if (stack->creature_id == monsterID[i])
               {
                  isSubjectToBeAttack = false;
                  break;
               }
         }
         else
            if (spell == SPL_CURSE)
            {
               int monsterID[] = { 52, 53, 114, 129, 130, 131, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 141 };

               for (int i = 0; i < 21; ++i)
                  if (stack->creature_id == monsterID[i])
                  {
                     isSubjectToBeAttack = false;
                     break;
                  }
            }
            else
               if (spell == SPL_BLIND || spell == SPL_SORROW)
               {
                  int monsterID[] = { 32, 33, 116, 117, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 141, 112, 113, 114, 115, 120, 121, 123, 125, 127, 129, 130, 131, 145, 146, 147, 148, 149 };
                  
                  for (int i = 0; i < 36; ++i)
                     if (stack->creature_id == monsterID[i])
                     {
                        isSubjectToBeAttack = false;
                        break;
                     }
                  if (spell == SPL_BLIND && (stack->creature_id == 70 || stack->creature_id == 71)) isSubjectToBeAttack = false;
                  if (spell == SPL_SORROW && (stack->creature_id == 40 || stack->creature_id == 41)) isSubjectToBeAttack = false;
               }
               else
                  if (spell == SPL_SLOW || spell == SPL_IMPLOSION)
                  {
                     int monsterID[] = { 145, 146, 147, 148, 149 };

                     for (int i = 0; i < 5; ++i)
                        if (stack->creature_id == monsterID[i])
                        {
                           isSubjectToBeAttack = false;
                           break;
                        }
                  }
                  else
                     if (spell == SPL_LIGHTNING_BOLT || spell == SPL_CHAIN_LIGHTNING || spell == SPL_TITANS_LIGHTNING_BOLT)
                     {
                        if (stack->creature_id == 113 || stack->creature_id == 125)
                           isSubjectToBeAttack = false;
                     }

   if (stack->creature_id == CID_BLACK_DRAGON || stack->creature_id == CID_MAGIC_ELEMENTAL || stack->creature_id == CID_GOLD_DRAGON && o_Spell[spell].level <= 4 ||
      (stack->creature_id == CID_GREEN_DRAGON || stack->creature_id == CID_RED_DRAGON || stack->creature_id == CID_AZURE_DRAGON) && o_Spell[spell].level <= 3 ||
      stack->active_spell_duration[SPL_ANTI_MAGIC] && o_Spell[spell].level <= effSchoolLevel + 2 ||
      (spell == SPL_FORGETFULNESS && (!(stack->creature.flags & BCF_CAN_SHOOT) || (stack->creature.flags & BCF_CAN_SHOOT) && stack->creature.shots == 0)) || !isSubjectToBeAttack)
      return false; else
      return true;
}

_BattleStack_* getEnemyStackToRedirectTo(_BattleStack_* stack, int spell)
{
   int sideFoe = 1 - stack->side;
   _BattleStack_* stackFoe = combatPowerOfStacks[stackIndex].second < o_BattleMgr->stacks_count[sideFoe] &&
   !o_BattleMgr->stack[sideFoe][combatPowerOfStacks[stackIndex].second].is_killed ? &o_BattleMgr->stack[sideFoe][combatPowerOfStacks[stackIndex].second] : 0;
   ++stackIndex;
   return stackFoe;
}

int __stdcall dataInit(LoHook* h, HookContext* c)
{
   showMagicMirrorAnim = false;
   showMagicResistAnim = false;
   memset(needMagicMirrorAnim, false, 40);
   memset(needMagicResistAnim, false, 40);

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

   combatPowerOfStacks.resize(o_BattleMgr->stacks_count[o_BattleMgr->current_side]);
   combatPowerOfStacksIsSpell.resize(o_BattleMgr->stacks_count[o_BattleMgr->current_side]);
      
   int count = 0;
   int countIsSpell = 0;

   for (int i = 0; i < combatPowerOfStacks.size(); ++i)
   {
      _BattleStack_* stack = &o_BattleMgr->stack[o_BattleMgr->current_side][i];

      if (checkingTheSelectedTarget(stack, spell))
      {
         if (stack->active_spell_duration[spell] == 0)
         {
            combatPowerOfStacks[count].first = o_pCreatureInfo[stack->creature_id].fight_value * stack->count_current;
            combatPowerOfStacks[count].second = i;
            count++;
         } else
         {
            combatPowerOfStacksIsSpell[countIsSpell].first = o_pCreatureInfo[stack->creature_id].fight_value * stack->count_current;
            combatPowerOfStacksIsSpell[countIsSpell].second = i;
            countIsSpell++;
         }
      }      
   }

   combatPowerOfStacks.resize(count);
   combatPowerOfStacksIsSpell.resize(countIsSpell);

   sort(combatPowerOfStacks.begin(), combatPowerOfStacks.end(), my_cmp);
   sort(combatPowerOfStacksIsSpell.begin(), combatPowerOfStacksIsSpell.end(), my_cmp);

   for (int i = 0; i < countIsSpell; ++i)
   {
      _BattleStack_* stack = &o_BattleMgr->stack[o_BattleMgr->current_side][combatPowerOfStacksIsSpell[i].second];
      combatPowerOfStacksIsSpell[i].first = stack->active_spell_duration[spell];
   }

   sort(combatPowerOfStacksIsSpell.begin(), combatPowerOfStacksIsSpell.end(), my_cmp);
   
   combatPowerOfStacks.resize(count + countIsSpell);

   for (int i = count; i < count + countIsSpell; ++i)
      combatPowerOfStacks[i] = combatPowerOfStacksIsSpell[count + countIsSpell - i - 1];
   
   stackIndex = 0;

   return EXEC_DEFAULT;
}
...
_PI->WriteLoHook(0x5A13D2, dataInit);


AlexSpl, добавьте, пожалуйста, вышеописанные функции в Ваш код и опубликуйте получившийся код для тестов. Заодно и потестируете их в работе. Если у Вас будет желание, то функции выбора целей (dataInit и newTarget) и функцию проверки (checkingTheSelectedTarget) Вы можете переписать более аккуратно. Но сам алогритм выбора цели лучше кардинально не менять. По-моему, он сейчас найболее оптимален.

Так будет удобней тестировать. А то мне каждый раз приходится отслежить ваши измнения/оптимизации кода и каждый раз копипастить из своего кода в ваш свои функции. В любом случае их придеться добавлять, так что пускай будет все в одном. :smile2:
Последний раз редактировалось Rolex 24 мар 2021, 17:46, всего редактировалось 4 раз(а).
Вернуться к началу

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 » 23 мар 2021, 14:28

New Magic Mirror (AlexSpl, Rolex)
версия для тестирования

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

using namespace std;

Patcher* _P;
PatcherInstance* _PI;

bool showMagicMirrorAnim, showMagicResistAnim, showSpellAnim, redirectAreaSpell;
bool needMagicMirrorAnim[2][20], needMagicResistAnim[2][20], needSpellAnim[2][20];
vector <pair<int, int>> combatPowerOfStacks;
vector <pair<int, int>> combatPowerOfStacksIsSpell;
int stackIndex;

#define SPL_DEATH_RIPPLE 24
#define CID_AZURE_DRAGON 132

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

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

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 NewMagicMirror =
{
   "{Магическое зеркало}\n\nЦелевой отряд способен отразить вражеское заклинание на самый сильный отряд противника (вместо случайного) с вероятностью в 40%.",
   "{Базовое Магическое зеркало}\n\nЦелевой отряд способен отразить вражеское заклинание на самый сильный отряд противника (вместо случайного) с вероятностью в 40%.",
   "{Продвинутое Магическое зеркало}\n\nЦелевой отряд способен отразить вражеское заклинание на самый сильный отряд противника (вместо случайного) с вероятностью в 50%.",
   "{Экспертное Магическое зеркало}\n\nВсе дружественные отряды способны отразить вражеское заклинание на все или самые сильные отряды противника (вместо случайного) с вероятностью в 50%."
};

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 = NewMagicMirror;

    return EXEC_DEFAULT;
}

bool my_cmp(const pair<int, int>& a, const pair<int, int>& b)
{
    return a.first >= b.first;
}

_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 checkingTheSelectedTarget(_BattleStack_* stack, int spell)
{
    int effSchoolLevel = stack->active_spells_power[SPL_ANTI_MAGIC];
    if ( effSchoolLevel == 0 ) effSchoolLevel = 1;

    bool isSubjectToBeAttacked = true;

    if ( spell == SPL_MAGIC_ARROW || spell == SPL_MISFORTUNE )
    {
      int monsterID[] = {52, 53, 114, 129, 130, 131};

      for (int i = 0; i < 6; ++i)
         if ( stack->creature_id == monsterID[i] )
         {
            isSubjectToBeAttacked = false;
            break;
         }
    }
    else
      if ( spell == SPL_ICE_BOLT )
      {
         if ( stack->creature_id == 115 || stack->creature_id == 123 )
            isSubjectToBeAttacked = false;
      }
      else
         if ( spell == SPL_FORGETFULNESS )
         {
            int monsterID[] = {41, 64, 65, 123, 127};

            for (int i = 0; i < 5; ++i)
               if ( stack->creature_id == monsterID[i] )
               {
                  isSubjectToBeAttacked = false;
                  break;
               }
         }
         else
            if ( spell == SPL_CURSE )
            {
               int monsterID[] = {52, 53, 114, 129, 130, 131, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 141};

               for (int i = 0; i < 21; ++i)
                  if ( stack->creature_id == monsterID[i] )
                  {
                     isSubjectToBeAttacked = false;
                     break;
                  }
            }
            else
               if ( spell == SPL_BLIND || spell == SPL_SORROW )
               {
                  int monsterID[] = {32, 33, 116, 117, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 141, 112, 113,
                      114, 115, 120, 121, 123, 125, 127, 129, 130, 131, 145, 146, 147, 148, 149};
                 
                  for (int i = 0; i < 38; ++i)
                     if ( stack->creature_id == monsterID[i] )
                     {
                        isSubjectToBeAttacked = false;
                        break;
                     }
                  if ( spell == SPL_BLIND && (stack->creature_id == 70 || stack->creature_id == 71) ) isSubjectToBeAttacked = false;
                  if ( spell == SPL_SORROW && (stack->creature_id == 40 || stack->creature_id == 41) ) isSubjectToBeAttacked = false;
               }
               else
                  if ( spell == SPL_SLOW || spell == SPL_IMPLOSION )
                  {
                     int monsterID[] = {145, 146, 147, 148, 149};

                     for (int i = 0; i < 5; ++i)
                        if ( stack->creature_id == monsterID[i] )
                        {
                           isSubjectToBeAttacked = false;
                           break;
                        }
                  }
                  else
                     if ( spell == SPL_LIGHTNING_BOLT || spell == SPL_CHAIN_LIGHTNING || spell == SPL_TITANS_LIGHTNING_BOLT )
                     {
                        if (stack->creature_id == 113 || stack->creature_id == 125)
                           isSubjectToBeAttacked = false;
                     }


    return !(stack->creature_id == CID_BLACK_DRAGON || stack->creature_id == CID_MAGIC_ELEMENTAL || stack->creature_id == CID_GOLD_DRAGON && o_Spell[spell].level <= 4 ||
            (stack->creature_id == CID_GREEN_DRAGON || stack->creature_id == CID_RED_DRAGON || stack->creature_id == CID_AZURE_DRAGON) && o_Spell[spell].level <= 3 ||
             stack->active_spell_duration[SPL_ANTI_MAGIC] && o_Spell[spell].level <= effSchoolLevel + 2 ||
            (spell == SPL_FORGETFULNESS && (!(stack->creature.flags & BCF_CAN_SHOOT) || (stack->creature.flags & BCF_CAN_SHOOT) && stack->creature.shots == 0)) || !isSubjectToBeAttacked);
}

char __stdcall massSpellMagicMirror(HiHook *h, int spell, int ssLevel)
{
    if ( spell == SPL_MAGIC_MIRROR && ssLevel == 3 )
        return FALSE; else
        return CALL_2(char, __fastcall, h->GetDefaultFunc(), spell, ssLevel);
}

bool magicMirrorLucky(const _BattleStack_* stack, const int spell)
{
    int effSchoolLevel = stack->active_spells_power[SPL_MAGIC_MIRROR];
   
    return !(o_Spell[spell].flags & SPF_FRIENDLY_HAS_MASS) && (stack->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) &&
        stack->active_spell_duration[SPL_MAGIC_MIRROR] > 0 && Randint(1, 100) <= o_Spell[SPL_MAGIC_MIRROR].effect[effSchoolLevel];
}

_BattleStack_* getEnemyStackToRedirectTo(_BattleStack_* stack, int spell)
{
    int sideFoe = 1 - stack->side;
    _BattleStack_* stackFoe = combatPowerOfStacks[stackIndex].second < o_BattleMgr->stacks_count[sideFoe] &&
    !o_BattleMgr->stack[sideFoe][combatPowerOfStacks[stackIndex].second].is_killed ? &o_BattleMgr->stack[sideFoe][combatPowerOfStacks[stackIndex].second] : 0;
    ++stackIndex;

    return stackFoe;
}

int __stdcall newTarget(LoHook* h, HookContext* c)
{
    int pos = 0;
    int minDuration = INT_MAX;

    int count = 0;

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

    combatPowerOfStacks.resize(o_BattleMgr->stacks_count[o_BattleMgr->current_side]);

    for (unsigned i = 0; i < combatPowerOfStacks.size(); ++i)
    {
      _BattleStack_* stack = &o_BattleMgr->stack[o_BattleMgr->current_side][i];

      if ( checkingTheSelectedTarget(stack, spell) )
      {
         combatPowerOfStacks[count].first = o_pCreatureInfo[stack->creature_id].fight_value * stack->count_current;
         combatPowerOfStacks[count].second = i;
         count++;
      }     
    }

    combatPowerOfStacks.resize(count);

    sort(combatPowerOfStacks.begin(), combatPowerOfStacks.end(), my_cmp);

    for (int i = 0; i < count; ++i)
    {
      _BattleStack_* stack = &o_BattleMgr->stack[o_BattleMgr->current_side][combatPowerOfStacks[i].second];
     
      if ( stack->active_spell_duration[spell] == 0 )
      {
         pos = combatPowerOfStacks[i].second;
         break;
      } else
      if ( stack->active_spell_duration[spell] < minDuration )
      {         
         minDuration = stack->active_spell_duration[spell];
         pos = combatPowerOfStacks[i].second;         
      }
    }

    c->eax = (int)&o_BattleMgr->stack[o_BattleMgr->current_side][pos];

    return EXEC_DEFAULT;
}

int __stdcall magicMirrorAI(LoHook* h, HookContext* c)
{
    _BattleStack_* stack = (_BattleStack_*)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 && Randint(1, 100) <= o_Spell[SPL_MAGIC_MIRROR].effect[effSchoolLevel] &&
            spell != SPL_TELEPORT && spell != SPL_SACRIFICE )
        {
            // Проигрываем одиночную анимацию
            CALL_5(void, __thiscall, 0x4963C0, o_BattleMgr, o_Spell[SPL_MAGIC_MIRROR].animation_ix, c->edi, 100, 0);

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

    return EXEC_DEFAULT;
}

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

    // Если не синглкаст
    if ( !c->edi )
    {
        showMagicMirrorAnim = false;
        showMagicResistAnim = false;
        showSpellAnim = false;
        redirectAreaSpell = false;
        memset(needMagicMirrorAnim, false, 40);
        memset(needMagicResistAnim, false, 40);
        memset(needSpellAnim, false, 40);
       
        combatPowerOfStacks.resize(o_BattleMgr->stacks_count[o_BattleMgr->current_side]);
        combatPowerOfStacksIsSpell.resize(o_BattleMgr->stacks_count[o_BattleMgr->current_side]);
       
        int count = 0;
        int countIsSpell = 0;

        for (unsigned i = 0; i < combatPowerOfStacks.size(); ++i)
        {
          _BattleStack_* stack = &o_BattleMgr->stack[o_BattleMgr->current_side][i];

          if ( checkingTheSelectedTarget(stack, spell) )
          {
             if ( stack->active_spell_duration[spell] == 0 )
             {
                combatPowerOfStacks[count].first = o_pCreatureInfo[stack->creature_id].fight_value * stack->count_current;
                combatPowerOfStacks[count].second = i;
                count++;
             } else
             {
                combatPowerOfStacksIsSpell[countIsSpell].first = o_pCreatureInfo[stack->creature_id].fight_value * stack->count_current;
                combatPowerOfStacksIsSpell[countIsSpell].second = i;
                countIsSpell++;
             }
          }     
        }

        combatPowerOfStacks.resize(count);
        combatPowerOfStacksIsSpell.resize(countIsSpell);

        sort(combatPowerOfStacks.begin(), combatPowerOfStacks.end(), my_cmp);
        sort(combatPowerOfStacksIsSpell.begin(), combatPowerOfStacksIsSpell.end(), my_cmp);

        for (int i = 0; i < countIsSpell; ++i)
        {
          _BattleStack_* stack = &o_BattleMgr->stack[o_BattleMgr->current_side][combatPowerOfStacksIsSpell[i].second];
          combatPowerOfStacksIsSpell[i].first = stack->active_spell_duration[spell];
        }

        sort(combatPowerOfStacksIsSpell.begin(), combatPowerOfStacksIsSpell.end(), my_cmp);

        combatPowerOfStacks.resize(count + countIsSpell);

        for (int i = count; i < count + countIsSpell; ++i)
          combatPowerOfStacks[i] = combatPowerOfStacksIsSpell[count + countIsSpell - i - 1];
       
       
        stackIndex = 0;

        if ( spell == SPL_DEATH_RIPPLE || spell == SPL_DESTROY_UNDEAD || spell == SPL_ARMAGEDDON ||
            switchCase == 17 || switchCase == 24 || o_BattleMgr->ShouldNotRenderBattle() )
        {
            c->return_address = 0x5A0646;
            return NO_EXEC_DEFAULT;
        }
    }
       
    return EXEC_DEFAULT;
}

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

    _ptr_ addr = h->GetAddress();
   
    if ( addr == hAddr[A_REFLECTSPELL_HOOK][E_MASS] )
    {
        stack = (_BattleStack_*)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 = *(_BattleStack_**)(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 = (_BattleStack_*)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 = (_BattleStack_*)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 = (_BattleStack_*)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 = (_BattleStack_*)c->esi;
        spell = SPL_ARMAGEDDON;
        c->return_address = hAddr[A_REFLECTSPELL_SKIP][E_ARMAGEDDON];
    }
   
    if ( stack )
    {
        bool isSpellSpecial = spell == SPL_FROST_RING || spell == SPL_FIREBALL || spell == SPL_INFERNO || spell == SPL_METEOR_SHOWER ||
             spell == SPL_DEATH_RIPPLE || spell == SPL_DESTROY_UNDEAD || spell == SPL_ARMAGEDDON;

        if ( magicMirrorLucky(stack, spell) )
        {
            showMagicMirrorAnim = true;
            needMagicMirrorAnim[stack->side][stack->index_on_side] = true;
           
            redirectAreaSpell = addr == hAddr[A_REFLECTSPELL_HOOK][E_AREA];
            if ( isSpellSpecial ) return NO_EXEC_DEFAULT;

            // Если сработало Magic Mirror, перенаправляем заклинание на вражеский отряд
            _BattleStack_* stackFoe = getEnemyStackToRedirectTo(stack, spell);
            if ( stackFoe )
            {
                // Определяем, сработает ли резист против отражённого заклинания
                int hitPercentage = (int)(100 * CALL_7(float, __thiscall, 0x5A83A0, o_BattleMgr, spell,
                    casterSide, stackFoe, 1, 1, *(int*)(c->ebp + 0x1C)));

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

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

                if ( isSpellSpecial ) return NO_EXEC_DEFAULT;
            }
        }
    }
   
    return NO_EXEC_DEFAULT;
}

int __stdcall massReflection(LoHook* h, HookContext* c)
{
    _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] ) c->return_address = hAddr[A_MASSANIMATION][E_DEATH_RIPPLE];
    if ( addr == hAddr[A_MASSREFLECTION_HOOK][E_DESTROY_UNDEAD] ) {
        c->edi = *(int*)(c->ebp - 0x10);
        c->return_address = hAddr[A_MASSANIMATION][E_DESTROY_UNDEAD];
    }

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

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

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

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

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);
   
    // Проигрываем массовую анимацию и звук резиста
    if ( showMagicResistAnim )
    {
        CALL_3(void, __fastcall, 0x59A890, "MagicRes.wav", -1, 3);
        CALL_4(void, __thiscall, 0x5A6AD0, o_BattleMgr, &needMagicResistAnim, 78, 0);
    }

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

    // Если заклинание отражено
    if ( redirectAreaSpell )
    {
        showMagicMirrorAnim = false;
        showMagicResistAnim = false;
        memset(needMagicMirrorAnim, false, 40);
        memset(needMagicResistAnim, false, 40);

        CALL_5(void, __thiscall, h->GetDefaultFunc(), battleMgr, o_BattleMgr->stack[o_BattleMgr->current_side][0].hex_ix, spell, a3, a4);

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

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

int __stdcall playMagicMirrorSound(LoHook* h, HookContext* c)
{
    CALL_3(void, __fastcall, 0x59A890, o_Spell[SPL_MAGIC_MIRROR].wav_name, -1, 3);
   
    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.NewMagicMirrorTest");

            // Пропускаем оригинальный звук заклинания, чтобы воспроизвезти его позже, и инициализируем данные
            _PI->WriteLoHook(0x5A0634, skipOrigSound_dataInit);

            // Синглкаст
            _PI->WriteLoHook(0x5A05CC, newTarget);
            _PI->WriteLoHook(0x5A058B, magicMirrorAI);
           
            // Масскаст
            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);
        }
    }

    return TRUE;
}

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

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

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

Сообщение Rolex » 23 мар 2021, 14:50

Код: Выделить всё
   if (stack)
   {
      bool isSpellSpecial = spell == SPL_FROST_RING || spell == SPL_FIREBALL || spell == SPL_INFERNO || spell == SPL_METEOR_SHOWER ||
         spell == SPL_DEATH_RIPPLE || spell == SPL_DESTROY_UNDEAD || spell == SPL_ARMAGEDDON;

      if (magicMirrorLucky(stack, spell))
      {
         showMagicMirrorAnim = true;
         needMagicMirrorAnim[stack->side][stack->index_on_side] = true;

         redirectAreaSpell = addr == hAddr[A_REFLECTSPELL_HOOK][E_AREA];
         if (isSpellSpecial) return NO_EXEC_DEFAULT;

         // Если сработало Magic Mirror, перенаправляем заклинание на вражеский отряд
         _BattleStack_* stackFoe = getEnemyStackToRedirectTo(stack, spell);
         if (stackFoe)
         {
            // Определяем, сработает ли резист против отражённого заклинания
            int hitPercentage = (int)(100 * CALL_7(float, __thiscall, 0x5A83A0, o_BattleMgr, spell,
               casterSide, stackFoe, 1, 1, *(int*)(c->ebp + 0x1C)));

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

При сборке кода в VS2015 проблема с reflectSpell в участке кода выше:
Цитата:
используется потенциально неинициализированная локальная переменная-указатель "stack"
используется потенциально неинициализированная локальная переменная-указатель "hero"
Вернуться к началу

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 » 23 мар 2021, 14:55

Это потому что вся инициализация "зашита" в тело условий. Можете до условий прописать stack = 0;
Вернуться к началу

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

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

Сообщение Rolex » 23 мар 2021, 15:02

Собрал. А на VS2008 и без инициализации собирается?
Вернуться к началу

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 » 23 мар 2021, 15:03

Да. Как и без _CRT_SECURE_NO_WARNINGS. В новых версиях гайки позакручивали :smile1:
Вернуться к началу

Пред.След.

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

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

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