Объявления

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

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

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

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

Сообщение Rolex » 05 апр 2021, 20:00

Такс, ну у нас же все равно в приоритете maxDamageFightValueDelta. Дельты равны у нас могут быть только в случаях одинаковых групп существ. Хотя... можгут быть исключения.
Но все равно собираем Дельты в одномерный массив. А в другом по каждому проверяему гексу, собираем кол-во доп единичек суммарно по всем отрядам из проверяемого гекса, которых мы сможет дополнительно уничтожить. Потом проходимся по массиву Дельт и в случае равенства Дельт, подключаем второй массив, и выбираем уже по нему тот варинт, где накопилось больше всего единичек.
Вернуться к началу

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 » 05 апр 2021, 20:02

Не слишком ли много кода для сомнительного результата? Я бы забил.

В процессе битвы такой алгоритм может выбрать 5 Титанов с 1 HP вместо 4 Титанов в группе. Но бой же не кончается. Добьют этого Титана. Главное, чтобы по ценности HP было всё норм.
Вернуться к началу

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

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

Сообщение Rolex » 05 апр 2021, 20:08

Ну да, сложность еще в том, что просто по сумме единичек мы сравнивать не можем, эти единички могут быть разного веса. Нужно сравнивать отряды попарно.

Ладно, что там у нас еще осталось?
Вернуться к началу

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 » 05 апр 2021, 20:15

Всё, вроде. Можно потестить эту функцию ещё:

Код: Выделить всё
double _BattleStackEx_::damageFightValue(const int spellDamage)
{
    return min(spellDamage, this->count_current * this->full_hp - this->lost_hp) * this->creature.fight_value;
}

Так. Исправил. Мы же урон не может больше spellDamage нанести :smile14:

Уже туплю :smile1: Первый вариант, вроде, правильный?
Последний раз редактировалось AlexSpl 05 апр 2021, 20:22, всего редактировалось 1 раз.
Вернуться к началу

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

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

Сообщение Rolex » 05 апр 2021, 20:19

Что делает эта функция?
Вернуться к началу

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 » 05 апр 2021, 20:25

Первый вариант правильный. Путаю урон и остаток здоровья.

Функция считает, сколько HP у отряда снимет заклинание. Каждая 1 HP стоит Fight Value существа в отряде. Эти величины потом суммируются по области (плюс вычитание аналогичной суммы для союзных отрядов в области).

Если я правильно понимаю, эта функция обеспечит каст заклинания на группу, в которой как можно больше сильных существ. Направлена на максимизацию урона, наносимого высокоуровневым юнитам (а они всегда самые опасные).
Последний раз редактировалось AlexSpl 05 апр 2021, 20:34, всего редактировалось 2 раз(а).
Вернуться к началу

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

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

Сообщение Rolex » 05 апр 2021, 20:30

Такс, а что если в случае нескольких одинаковых maxDamageFightValueDelta, мы по каждой группе каждую единичку умножаем ее Fight Value и суммуируем все Fight Value по каждой единичке в каждой группе. И храним эти суммы Fight Value единичек в другом массиве. То есть нам по сути понадобится всего лишь два массива или же один вектор pair и все.
***
В первом массиве проходимся по всем одинаковым макс Дельтам и в случае равенства выбираем из другого массива макс по сумме Fight Value единичек.
Вернуться к началу

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 » 05 апр 2021, 20:37

Я бы не бился с единичками. Вот, правда, не стоит того. Или Ваша текущая функция, или та, что выше. Если хотите заморочиться, не буду отговаривать. Но задача очень трудоёмкая.
Вернуться к началу

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

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

Сообщение Rolex » 05 апр 2021, 20:42

Я подумаю еще. Опубликуйте, пожалуйста, тот код, который у Вас сейчас есть со всеми исправлениями.
Вернуться к началу

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 » 05 апр 2021, 20:48

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

using namespace std;

Patcher* _P;
PatcherInstance* _PI;

struct _BattleStackEx_ : public _BattleStack_
{
    int getMonsterSpell();
    bool approvedTarget(const int spell);
    bool magicMirrorLucky(const int spell);
    _BattleStackEx_* getStackToRedirect(const int spell, const bool massCast = true);
    int calcMagicDamage(const int spell, const _Hero_* attHero, const _Hero_* defHero);
    inline int fightValue() {
        return this->creature.fight_value * this->count_current;
    };
    double damageFightValue(const int spellDamage);
    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 getHitChance(const int spell) {
        return (int)(100 * getMagicVulnerability(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);
    }
};

bool showMagicMirrorAnim, showMagicResistAnim, showSpellAnim, redirectedAreaSpell;
bool needMagicMirrorAnim[2][20], needMagicResistAnim[2][20], needSpellAnim[2][20];
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;
}

int Randint_s()
{
    unsigned dice;
    rand_s(&dice);
    return (int)((double)dice / ((double)UINT_MAX + 1) * 100.0) + 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);
}

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

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 && Randint_s() <= 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;
}

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

double _BattleStackEx_::damageFightValue(const int spellDamage)
{
    double dmgFightValue;

    // Если урон заклинания в точности равен здоровью отряда
    if ( spellDamage == this->count_current * this->full_hp - this->lost_hp )
        dmgFightValue = this->fightValue();
    else
        dmgFightValue = min((double)spellDamage / this->full_hp,
        this->count_current - (double)this->lost_hp / this->full_hp) * this->creature.fight_value;

    return dmgFightValue;
}

// Потестируйте эту функцию тоже
/* double _BattleStackEx_::damageFightValue(const int spellDamage)
{
    return min(spellDamage, this->count_current * this->full_hp - this->lost_hp) * this->creature.fight_value;
} */

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

    return EXEC_DEFAULT;
}

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

bool cmpFightValue(_BattleStackEx_* a, _BattleStackEx_* b)
{
    return a->creature.fight_value * a->count_current > b->creature.fight_value * b->count_current;
}

// Костыль для C++98
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];
    }
};

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 _BattleStackEx_::getMonsterSpell()
{
    int spell = ID_NONE;

    if ( this )
    {
        switch ( this->creature_id )
        {
        case CID_THUNDERBIRD:
            spell = SPL_LIGHTNING_BOLT;
            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;
        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);
        sort(stackVector.begin(), stackVector.end(), cmpFightValue);
        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 && Randint_s() <= 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;
}

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 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;
               
    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 damageFightValueUs = 0.0, damageFightValueThem = 0.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) )
                    {
                        // Считаем магический урон, получаемый стеком stack
                        int spellDamage = stack->calcMagicDamage(spell, attHero, defHero);

                        if ( stack->side == o_BattleMgr->current_side ) {
                            ++countThem;
                            damageFightValueThem += stack->damageFightValue(spellDamage);
                        }
                        else {
                            ++countUs;
                            damageFightValueUs += stack->damageFightValue(spellDamage);
                        }

                        // Debug (проверяем урон)
                        showDebugInfo("{Epicenter Hex = %d}\n\nArea Hex = %d\n%d %s: %d\nThem = %0.2f\nUs = %0.2f", hex, iHex,
                            stack->count_current, GetCreatureName(stack->creature_id, stack->count_current),
                            spellDamage, damageFightValueThem, damageFightValueUs);
                    }
                                   
                    if ( i + 1 < areaSize )
                    {
                        int iHexNext = hex + area[i + 1];
                        if ( isValidHex(iHexNext) && (_BattleStackEx_*)o_BattleMgr->hex[iHexNext].GetCreature() == stack ) ++i;
                    }
                }
            }
        }

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

            // Debug (проверяем damageFightValue)
            showDebugInfo("{NEW MAX FOUND!\nEpicenter Hex = %d}\nx = %d, y = %d\n\nThem = %0.2f\nUs = %0.2f\nThem - Us = %0.2f\nMax = %0.2f",
                hex, hex % 17, hex / 17, damageFightValueThem, damageFightValueUs,
                damageFightValueThem - damageFightValueUs, maxDamageFightValueDelta);
        }
    }

    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 playMagicMirrorSound(LoHook* h, HookContext* c)
{
    playSound(o_Spell[SPL_MAGIC_MIRROR].wav_name);
   
    return EXEC_DEFAULT;
}

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->getHitChance(spell);

                if ( Randint_s() <= hitChanceFoe )
                {
                    showSpellAnim = true;
                    needSpellAnim[stackFoe->side][stackFoe->index_on_side] = true;
                    CALL_5(void, __thiscall, 0x444610, stackFoe, spell, spellPower, schoolLevel, hero);
                }
                else if ( hitChanceFoe )
                {
                    showMagicResistAnim = true;
                    needMagicResistAnim[stackFoe->side][stackFoe->index_on_side] = true;
                }
            }
        }
        else
        {
            if ( Randint_s() <= 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_LIGHTNING_BOLT;
        addrHit = 0x440F03;
        addrSkip = 0x4412AB;
        break;
    case 0x440372:
        spell = SPL_BLIND;
        addrHit = 0x44038D;
        addrSkip = 0x4402AE;
        break;
    case 0x4404DB:
        spell = SPL_CURSE;
        addrHit = 0x4404F6;
        addrSkip = 0x4402AE;
        break;
    }

    if ( stack->magicMirrorLucky(spell) )
    {
        c->return_address = addrSkip;
       
        playSound(o_Spell[SPL_MAGIC_MIRROR].wav_name);
        stack->playAnimation(o_Spell[SPL_MAGIC_MIRROR].animation_ix);
       
        // Если сработало Magic Mirror, перенаправляем заклинание на вражеский отряд
        _BattleStackEx_* stackFoe = stack->getStackToRedirect(spell, false);
        if ( stackFoe )
        {
            // Определяем, сработает ли резист против отражённого заклинания
            int hitChanceFoe = stackFoe->getHitChance(spell);

            if ( Randint_s() <= hitChanceFoe )
            {
                c->edi = (int)stackFoe;
                c->return_address = addrHit;
            }
            else if ( hitChanceFoe )
            {
                playSound("MagicRes.wav");
                stackFoe->playAnimation(78);
            }
        }
       
        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.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);

            // Масскаст
            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(0x4404DB, mirrorMonsterSpell); // Curse
        }
    }

    return TRUE;
}

showDebugInfo() можно закомментить после тестов. Ещё гляньте на закомменченную функцию damageFightValue() для сравнения (это метод с альтернативной функцией).
Вернуться к началу

Пред.След.

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

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

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