Объявления

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

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

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

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

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

AlexSpl писал(а):

Я привёл другой пример. Помните: представляем крайность. 1 копейщик + 100 грифонов под слепью. Всё, как выше. Ударное заклинание должно выбрать копейщика, пусть оно и сносит 50 грифонов, т.к. это наш единственный шанс на победу.

ВОТ ИМЕННО! Я ж вам именно за это и говорю уже в который раз! Ударное заклинание и выберет Копейщика, если это ударное - синглкаст, а если площадное, то оно выберет уже Грифонов. Так вот я и хочу, чтобы вы исправили это для площадных. Чтобы и площадные в данном случае выбирали Копейщика. Чтобы ослеплённые, парализованные и окаменевшие отряды не попадали в stackVector и для дамажащих площадных.
Вернуться к началу

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 » 13 апр 2021, 17:26

Так это исправить очень легко. Нужно добавить проверки spell == SPL_FIREBALL и т.д. в dataInit(), где идёт заполнение вектора.

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

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 » 13 апр 2021, 17:40

Пробуйте этот код:

 
Код: Выделить всё
#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);
    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->current_health() && spellDamage < this->full_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;
}

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

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 && Randint_s() <= 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 ( 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_THUNDERBOLT;
        addrHit = 0x440F03;
        addrSkip = 0x4412AB;
        break;
    case 0x440372:
        spell = SPL_BLIND;
        addrHit = 0x44038D;
        addrSkip = 0x4402AE;
        break;
    case 0x440467:
        spell = SPL_CURSE;
        addrHit = 0x440482;
        addrSkip = 0x4402AE;
        break;
    }

    if ( stack->magicMirrorLucky(spell) )
    {
        c->return_address = addrSkip;
       
        // Если сработало Magic Mirror, отражаем обратно
        _BattleStackEx_* stackFoe = (_BattleStackEx_*)c->esi;
        if ( stackFoe )
        {
            int hitChanceFoe = stackFoe->getHitChanceResist(spell);
           
            if ( Randint_s() <= 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;
}

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

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

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

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

AlexSpl писал(а):

Если пропускает временно выведенные из боя отряды, то дальше предлагаю не улучшать

Всмысле временно выведенные из боя отряды? Это под Слепотой, Параличем и Окамянением?
Вернуться к началу

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 » 13 апр 2021, 18:52

Да. Я потестил. Их не трогает.
Вернуться к началу

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

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

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

AlexSpl писал(а):

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

ОК. Тогда за Вами еще раз полное всесторонее комплексное тестирование всего плагина. Может еще где найдете баги. Я со своей стороны уже проводил тестирование. Ну а то, что мы не нашли, найдется уже в реальных боях, как, например, получилось с Палаткой.

PS: Кстати, а Зеркало на Базовом и Продвинутом уровнях, где вероятность отражения 40% и оно может быть наложено только на один или на несколько отрядов (разными кастами), но, например, не на все. Там все ок считается/отражается?

PPS: После Вашего переноса перестали работать единички для площадных! У меня все работало. Нужно исправлять. Можно быстро проверить на двух парах (5 Архов + 5 Титанов в каждой паре). Нижнюю пару немного повредить, чтобы площадным потом хватило убить 1 существо из нижней пары, но ни одного из верхней. Должно выбрать нижнюю пару.

И, кстати, единички нужно сделать и для ударных синглкастов и вот тогда можно будет остановится.
Вернуться к началу

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

Код: Выделить всё
if ( spellDamage % stack->full_hp != 0 && spellDamage / stack->full_hp < stack->count_current && spellDamage % stack->full_hp >= stack->full_hp - stack->lost_hp )
    onesFightValueThem += o_pCreatureInfo[stack->creature_id].fight_value;

Вы можете своими словами это рассказать? Т.е. за что отвечает каждое условие выше?

В коде я переписал на это (Вы, вроде, говорили, что тщательно тестили):

Код: Выделить всё
inline double extraFightValue(const int spell, const int spellDamage) {
    return spellDamage >= this->current_health() && spellDamage < this->full_health() ?
        this->getFullVulnerability(spell) * this->creature.fight_value : 0;
}

Попробуйте ещё заменить >= на >. Сейчас это работает, когда урон от заклинания не меньше текущего здоровья отряда, но меньше его полного здоровья.

Цитата:
PS: Кстати, а Зеркало на Базовом и Продвинутом уровнях, где вероятность отражения 40% и оно может быть наложено только на один или на несколько отрядов (разными кастами), но, например, не на все. Там все ок считается/отражается?

Да.
Вернуться к началу

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

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

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

1. ( spellDamage % stack->full_hp != 0 ) - если есть остаток урона от закла при делении на полное здоровье 1 существа в проверяемом стеке. То есть если есть какой-то остаток, чтобы проверить его, тогда идем дальше и проверяем его.

2. ( spellDamage / stack->full_hp < stack->count_current ) - если урон от закла при делении на полное здоровье 1 существа из проверяемого стека будет строго меньше кол-ва существ в стеке. Например есть 5 Титанов с общим здоровьем 1500 ед. При уроне от закла 1499, посчитает как 4 < 5 (дробная часть отбросится). Если урон от закла 1500 и более, тогда далее и не проверяем, стек будет полностью убит, а в таком случае нам выгодней взять из другой группы (хотя при равенстве без разницы).

3. ( spellDamage % stack->full_hp >= stack->full_hp - stack->lost_hp ) - если остаток урона от закла при делении на полное здоровье 1 существа в проверяемом стеке будет более или равно остатку здоровья верхнего существа в стеке, то плюсуем его Fight Value;

Если, например, урон от закла 1600 и у нас 5 титанов, но у верхнего осталось 100 ед здоровья, то этот стек нам не выгодно брать. Нам выгодней взять тогда стек с другой группы с полным здоровьем. То есть в таком случае не пройдет по 2 пункту.

AlexSpl писал(а):

В коде я переписал на это (Вы, вроде, говорили, что тщательно тестили):

Все, кроме единичек. Единички тестил только в своем решении. Думал, после переноса проблем не будет.
Вернуться к началу

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

Т.е. правильно ли я понимаю: добавляем extraFightValue (Fight Value одного существа из отряда), если заклинание а) не убивает отряд полностью (это условие уже есть выше); б) убивает верхнее существо в отряде, если у него не полное здоровье?
Вернуться к началу

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

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

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

Да, все верно. Только extraFightValue мы не плюсуем к damageFightValue, это просто дополнительная проверка при равенстве maxDamageFightValueDelta.
Вернуться к началу

Пред.След.

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

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

Сейчас этот форум просматривают: Bing [Bot] и гости: 2

cron