Объявления
Поздравляем
Roman2211


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

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

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

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

Сообщение AlexSpl » 13 мар 2021, 20:47

Цитата:
Но целью была проверка силы в максимально равных условиях БЕЗ использования какой-либо магии.

Какие же это равные условия? Чёрные драконы в таких условиях просто мясо с продолженной атакой (да и та не помогает, если бой 1 на 1), а Древние чудища по-прежнему игнорят 80% защиты. Нужно тестировать комплексно. Равные были бы, если абилки отнять, а оставить только размер и статы.

По масскасту. Было бы удобно, если бы функция умела проигрывать разные анимации одновременно. Тогда, например, для тех отрядов, которые не отразили магию, проигрывалась бы анимация Замедления, а для остальных - Магического зеркала. А как с резистом дела обстоят при масскасте? Тоже не работает?
Вернуться к началу

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

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

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

AlexSpl писал(а):

Какие же это равные условия? Чёрные драконы в таких условиях просто мясо с продолженной атакой (да и та не помогает, если бой 1 на 1), а Древние чудища по-прежнему игнорят 80% защиты. Нужно тестировать комплексно. Равные были бы, если абилки отнять, а оставить только размер и статы.

Ну отчасти я с вами согласен. В том плане, что Древние чудища используют свою абилку по полной, тогда как Черные драконы не могут ее использовать в свою пользу при таком тестировании. Но тестирование с Армагеддоном как бы тоже сложно назвать равными условиями. Ну а если забрать у Древних чудищ их абилку, то мясом уже будут чидища, а не драконы. Без своей абилки они разве что Призрачных драконов и Фениксов смогли бы одолеть. Но, по-моему, это проблема самой абилки, вернее ее имбалансности. Слишком высокий процент разрабы сделали. Оптимально было бы снимать 25%/50% защиты вместо 40%/80%.

AlexSpl писал(а):

По масскасту. Было бы удобно, если бы функция умела проигрывать разные анимации одновременно. Тогда, например, для тех отрядов, которые не отразили магию, проигрывалась бы анимация Замедления, а для остальных - Магического зеркала.

Ну а что если научить ее это делать? Ну в смысле доработать ее или написать свою, которая бы заменила имеющуюся и умела бы проигрывать одновременно анимацию разных заклинаний. Разработчики по каким-то причинам не смогли этого сделать (или не захотели). Может у вас получится. Или слишком сложно?

AlexSpl писал(а):

А как с резистом дела обстоят при масскасте? Тоже не работает?

Никак. Заклинание в таком случае просто не накладывается на такой отряд и анимация сопротивления для него при масскасте не проигрывается.

Например, если у врага Черные драконы и Боевые гномы с 40% сопротивлением к магии. И если мы накладываем обычное Замедление (не Эксперт) на один отряд, например, на гномов и если они отражают его, то проигрывается анимация сопротивления (щита) и соответствующее звуковое сопровождение. Если же мы накладываем массовое Замедление, то в случае отражения его гномами (драконы в любом случае его игнорят без анимации сопротивления), анимация сопротивления как для драконов, так и для гномов не проигрывается, точно также как не проигрывается и анимация Замедления, так как ни на кого оно не было наложено. В таком случае проигрывается только звуковое сопровождение заклинания Замедление без какой-либо анимации. Хотя, по идее, для обоих отрядов должна проигрыватся анимация сопротивления (щита) и звуковое сопровождение именно сопротивления.

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

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

Пока код масскаста не смотрел, но, думаю, судя по таким заклинаниям, как Destroy Undead и Death Ripple, сначала заполняется специальный массив менеджера битвы (оффсет 0x547C), где отмечаются существа, на которых заклинание подействует. Не трогая пока проблему одновременного проигрывания разных анимаций, можно в самый конец функции добавить ещё одну проверку на Magic Mirror, которая будет с заданной вероятностью исключать из этого массива отряды, находящиеся под действием этого заклинания, и включать вместо них вражеские отряды. Думаю, это будет просто реализовать.
Вернуться к началу

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

Пока вот что получилось (анимация последовательная, звук кастуемого заклинания):

Код: Выделить всё
#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)))

bool showMagicMirrorAnim;
bool needMagicMirrorAnim[2][20];

bool magicMirrorLucky(_Hero_* hero, _BattleStack_* stack)
{
    int effSchoolLevel = CALL_3(int, __thiscall, 0x4E52F0, hero, SPL_MAGIC_MIRROR, o_BattleMgr->spec_terr_type);
   
    // Уберите рандомную составляющую на время тестов
    return stack->side != o_BattleMgr->current_side && stack->active_spell_duration[SPL_MAGIC_MIRROR] > 0 &&
        Randint(1, 100) <= o_Spell[SPL_MAGIC_MIRROR].effect[effSchoolLevel];
}

_BattleStack_* getEnemyStackToRedirectTo(_BattleStack_* stack)
{
    // В целях демонстрации работы просто перенаправим заклинание на вражеский отряд
    // с таким же индексом, как у отряда, отразившего заклинание,
    // поэтому тестируйте с полными армиями во избежание сюрпризов
    return &o_BattleMgr->stack[1 - stack->side][stack->index_on_side]; 
}

int __stdcall ResetMagicMirrorFlags(LoHook* h, HookContext* c)
{
    showMagicMirrorAnim = false;
    memset(needMagicMirrorAnim, false, 40);
   
    return EXEC_DEFAULT;
}

int __stdcall ReflectSpell(LoHook* h, HookContext* c)
{
    _BattleStack_* stack = (_BattleStack_*)c->esi;
    _BattleStack_* stackFoe;

    int spell = *(int*)(c->ebp + 0xC);
    int spellPower = *(int*)(c->ebp + 0x14);
    int schoolLevel = *(int*)(c->ebp + 0x10);
    _Hero_* hero = *(_Hero_**)(c->ebp + 8);
   
    // Если сработало Magic Mirror, перенаправляем заклинание на вражеский отряд
    if ( magicMirrorLucky(o_BattleMgr->hero[1 - o_BattleMgr->current_side], stack) ) {
        showMagicMirrorAnim = true;
        needMagicMirrorAnim[stack->side][stack->index_on_side] = true; 
        stackFoe = getEnemyStackToRedirectTo(stack);
        CALL_5(void, __thiscall, 0x444610, stackFoe, spell, spellPower, schoolLevel, hero);
        AnimateStack[stackFoe->side][stackFoe->index_on_side] = true;
    }
    else if ( Randint(1, 100) <= c->eax ) {
        // Если не сработал резист, применяем эффект заклинания
        CALL_5(void, __thiscall, 0x444610, stack, spell, spellPower, schoolLevel, hero);
        AnimateStack[stack->side][stack->index_on_side] = true;
    }
   
    c->return_address = 0x5A6A83;
    return NO_EXEC_DEFAULT;
}

int __stdcall MagicMirrorMassReflection(LoHook* h, HookContext* c)
{
    // Проигрываем массовую анимацию Magic Mirror
    if ( showMagicMirrorAnim )
        CALL_4(void, __thiscall, 0x5A6AD0, o_BattleMgr, &needMagicMirrorAnim, o_Spell[SPL_MAGIC_MIRROR].animation_ix, 0);

    return EXEC_DEFAULT;
}

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

    if ( DLL_PROCESS_ATTACH == ul_reason_for_call )
    {
        if ( !plugin_On )
        {
            plugin_On = true;
            _P = GetPatcher();
            _PI = _P->CreateInstance((char*)"HD.Plugin.NewMagicMirrorTest");
           
            _PI->WriteLoHook(0x5A13D2, ResetMagicMirrorFlags);
            _PI->WriteLoHook(0x5A6A50, ReflectSpell);
            _PI->WriteLoHook(0x5A13E5, MagicMirrorMassReflection);
        }
    }

    return TRUE;
}

В процессе обнаружил, что 0x547C - это байтовый массив (char/bool). Исправил код палатки в теме с пользовательскими плагинами.
Вернуться к началу

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

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

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

1) Кейс на котором не работает. 2 отряда против 4. У 4 стоит Magic Mirror. Накладываю на всех 4 Замедление, 3 из 4 отражают его. В итоге 1 должно отразится в никуда, а 2 на все мои отряды (которых 2), а отражает всего на 1. Когда отражает 2 из 4, должно опять же наложится на все отряды (их всего 2), а в итоге не накладывает ни на один отряд. В то время когда отражает 1, то на 1 накладывает.

2) Забавный баг. Когда мы накладываем Magic Mirror или любое другое положительное заклинание масскастом на свои отряды, но при этом на вражеские отряды уже наложено Magic Mirror, то получается перед тем, как наложить этот закл на свои отряды проигрывается амимация Magic Mirror для вражеских отрядов и только после этого проигрывается анимация этого заклинания и оно накладывается на отряды кастующего. То есть получается перед тем как проигрывать анимацию отражения Magic Mirror нужно проверять какое заклинание кастуется отрицательное/положительное, на свои/вражески отряды и есть ли у врага Magic Mirror.

3) Замедление или другие низкоуровневые заклинания могут отразится на драконов (кроме Черного) с иммунитетом от этого заклинания. Здесь нам нужно еще дополнительно обработать Лазурных, Красных и Зеленых драконов у которых иммунитет к заклинаниям 1-3 уровня, а также Золотых драконов у которых иммунитет к заклинаниям 1-4 уровня. Плюс в моем случае если на отряд наложена Антимагия, то отряд полностью игнорится и берется следующий по силе.

Но в данном случае правильней было бы сравнивать уровень направляемого заклинания с уровнем наложенной Антимагии. Так как нулевой уровень или базовый - это защита от заклов 1-3 уровня, а если закл 4-5 уровня, то можно брать этих существ в качестве цели. Если Антимагия Экпертная, тогда и сравнивать нет смысла. С драконами аналогично, если уровень закла выше чем тот уровень до которого у них иммунитет, то оставляем их в качестве цели, иначе переходим к следующему по силе отряду.

4) И получается в вашем случае при отражении берутся вражеские отряды с теми индексами, как у отрядов, которые отразили его, то есть получается без учета силы отрядов (хотя да, вы написали, что это тестовая версия... в целях демонстрации работы просто перенаправим заклинание на вражеский отряд с таким же индексом, как у отряда, отразившего заклинание). Здесь лучше будет сделать так, как с одним отрядом. То есть, если допустим отразило X, то мы накладываем не на первых X, а на Х самых сильных. Если конечно кол-во отражений (Х) не больше того кол-ва отрядов на которое должно пойти отражение. То есть можно завести еще один массив и отобрать индексы тех X отрядов из combatPowerOfStacks[i].second, которые удовлетворяют заданным условиям (проверка драконов и Антимагии).

Можете взять ту функцию, которую я писал и немного ее доработать или переписать. Порядковые номера всех отрядов в порядке убывания их силы хранятся в combatPowerOfStacks[i].second.

Код: Выделить всё
bool my_cmp(const pair<int, int>& a, const pair<int, int>& b)
{
   return a.first > b.first;
}

int __stdcall NewTarget(LoHook* h, HookContext* c)
{   
   int pos = 0;
   vector <pair<int, int> > combatPowerOfStacks;

   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];      
      combatPowerOfStacks[i].first = o_pCreatureInfo[stack->creature_id].fight_value * stack->count_current;
      combatPowerOfStacks[i].second = i;   
   }
   
   sort(combatPowerOfStacks.begin(), combatPowerOfStacks.end(), my_cmp);
   
   for (int i = 0; i < combatPowerOfStacks.size(); ++i)
   {
      _BattleStack_* stack = &o_BattleMgr->stack[o_BattleMgr->current_side][combatPowerOfStacks[i].second];
   
      if (stack->creature_id == CID_BLACK_DRAGON || stack->creature_id == CID_MAGIC_ELEMENTAL || stack->active_spell_duration[SPL_ANTI_MAGIC]) continue; else
      {
         pos = combatPowerOfStacks[i].second;
         break;
      }
   }

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

   return EXEC_DEFAULT;
}


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

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

Все нюансы можно учесть в двух функциях: magicMirrorLucky() и getEnemyStackToRedirectTo(). Первая отвечает за то, сработает ли Magic Mirror вообще, а вторая выбирает цель, на которую отразить заклинание. Если их грамотно написать, багов не будет :smile1: Свойства заклинания можно посмотреть в o_Spell[SPL_X].flags, описание флагов есть в ERM help (есть онлайн-версия, если кто не в курсе).
Вернуться к началу

offlineigrik  
Подмастерье
Подмастерье
 
Сообщения: 108
Зарегистрирован: 14 сен 2017, 12:35
Пол: Не указан
Поблагодарили: 84 раз.

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

Сообщение igrik » 16 мар 2021, 03:00

AlexSpl писал(а):

В Свойства заклинания можно посмотреть в o_Spell[SPL_X].flags, описание флагов есть в ERM help (есть онлайн-версия, если кто не в курсе).

Вот сылка на справочник: https://yadi.sk/d/IBjU0hh5AvUzdQ
Кнопка SS:F
Изображение
Вернуться к началу

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

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

Сообщение AlexSpl » 16 мар 2021, 11:09

Массовая анимация реализуется той же функцией, что и обычная: CombatMan_AnimationStep (sub_5A6E49). Эта функция берёт информацию о том, что анимировать прямо из структуры менеджера боя, а там нет полей под анимацию каждого отряда (есть два поля, общих для всех):

Код: Выделить всё
// Указатель на структуру с информацией об анимации (на входе - имя def с анимацией)
o_BattleMgr->Field<int>(0x132E8) = CALL_1(int, __fastcall, 0x55C9C0, ((char**)0x641E18)[3 * o_Spell[SPL_MAGIC_MIRROR].animation_ix]);
// Номер анимации
o_BattleMgr->Field<int>(0x132EC) = o_Spell[SPL_MAGIC_MIRROR].animation_ix;

Т.е. без существенной доработки* этой функции, реализовать разную анимацию одновременно не получится.

* Нужно спускаться на уровень отрисовки кадра для каждого конкретного отряда. Может, кто-то однажды заморочится (но это уже уровень хорошего мода, типа HotA) :smile1: Я лично не готов тратить время на это, тем более последовательная анимация смотрится достаточно приемлемо.

Кстати, можно ещё анимировать резист при масскасте. Идея та же: заводим массив для отрядов, которые отразили заклинание, и в функцию MagicMirrorMassReflection() (только теперь её как-нибудь по-другому будет нужно назвать) добавляем вызов анимации масскаста для резиста.
Последний раз редактировалось AlexSpl 16 мар 2021, 13:01, всего редактировалось 1 раз.
Вернуться к началу

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

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

Сообщение Rolex » 16 мар 2021, 11:16

AlexSpl писал(а):

Все нюансы можно учесть в двух функциях: magicMirrorLucky() и getEnemyStackToRedirectTo(). Первая отвечает за то, сработает ли Magic Mirror вообще, а вторая выбирает цель, на которую отразить заклинание. Если их грамотно написать, багов не будет :smile1:

Вот в том-то и дело, что грамотно их написать могут единицы.

AlexSpl писал(а):

Т.е. без существенной доработки* этой функции, получить разную анимацию одновременно не получится*. * Нужно спускаться на уровень отрисовки кадра для каждого конкретного отряда. Может, кто-то однажды заморочится (но это уже уровень хорошего мода, типа HotA) :smile1: Я лично не готов тратить время на это, тем более последовательная анимация смотрится достаточно приемлемо.

Ок. И так сойдет, главное сейчас с остальными пунктами разобраться и довести все до ума.

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

Цитата:
Ок. И так сойдет, главное сейчас с остальными пунктами разобраться и довести все до ума.

А как в HotA? Резист тоже не анимируется? Если ещё не сделали, подкиньте команде идейку. Было бы реально здорово видеть анимацию резиста частью массовой анимации. Как бы команда на меньшее и не согласится :smile2:
Вернуться к началу

Пред.След.

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

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

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

cron