Объявления

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

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

Герои Меча и Магии III: Возрождение Эрафии, Герои Меча и Магии III Дыхание Смерти, Герои Меча и Магии III Клинок Армагеддона, Герои Меча и Магии III Хроники Героев
onlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5538
Зарегистрирован: 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)
Поблагодарили: 2155 раз.

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

Сообщение AlexSpl » 19 авг 2021, 11:33

Цитата:
Мне вот интересно, а добавить совершенно новый вторичный навык или совершенно новый артефакт (не сборник!) сложно ли будет при наличии уже готовых картинок для этого дела? Сложнее нового закла или легче?

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

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

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

Сообщение Rolex » 19 авг 2021, 11:49

AlexSpl писал(а):

Можете проверить у себя?

У меня картинка есть.

AlexSpl писал(а):

Так, я понял. Без картинки - это когда герой уже знает заклинание :smile4:

Именно. Только в вашем случае там должно быть текстовое сообщение, вроде:
"... обучают Вас заклинанию - Bless. К сожалению, это заклинание вам уже знакомо ..."
Вернуться к началу

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

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

Сообщение Rolex » 19 авг 2021, 12:42

AlexSpl писал(а):

Вторичный навык, может быть, сложнее, а вот артефакт точно легче.

Кстати, а как вы смотрите на то, чтобы как-нибудь в будущем попробовать добавить 2 абсолютно новых вторичных навыка и 3 новых артефакта?

В Героях 3 есть 4-5 артов, которые повышаюют Мораль и Удачу героя. Но вот таких артов, которые бы снижали Мораль и Удачу врага, нет... И это не очень хорошо. В то время как в 5 Героях таких полно. Плюс в Героях 5 есть навыки, которые снижают Удачу и Мораль врага.

Вот у меня появилась идея попробовать добавить как-нибудь парочку вторичных навыков:
Неудача: снижает Удачу врага на -1/-2/-3;
Уныние: снижает Мораль врага на -1/-2/-3;

Плюс 3 арта:
Проклятое кольцо: -2 к Удаче противника.
Кольцо сломленного духа: -2 к Боевому духу противника.
Плащ смертоносной тени: -2 к Удаче и Боевому духу противника.

Картинки плаща и кольца можно взять из других артов в Героях 3 и после их изменить немного в графическом редакторе и получатся новые. :smile1: Подобные плагины внесут огромный вклад в баланс модификаторов Удачи и Морали в Героях 3 вместе с уже готовым плагином отрицательной Удачи. Но это как-нибудь потом, сейчас надо бы добить уже начатое с NewSpells и не отвлекаться на другое.
Вернуться к началу

onlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5538
Зарегистрирован: 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)
Поблагодарили: 2155 раз.

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

Сообщение AlexSpl » 19 авг 2021, 18:23

Нашёл причину крэша 0x4336C0. Описка: вместо _PI->WriteDword(0x433719, 0x3EA); поставил на начало функции :smile14: Как и в случае с 0x439330 (надо _PI->WriteDword(0x43940C, 0x3EA);).

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

Теперь не вылетает в квике:

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

#define SPELLS_MAX 128
#define ARTIFACTS_NUM 144
#define SPELLS_NUM 71
#define SPL_FEAR 70

Patcher* _P;
PatcherInstance* _PI;

struct _ComboArtInfo_ {
   int index;
   std::bitset<160> parts;
};

#define o_ComboArtInfo (*(_ComboArtInfo_**)0x660B6C)

_Spell_ spell[SPELLS_MAX];
//char iniPath[MAX_PATH];

int __stdcall afterInit(LoHook* h, HookContext* c)
{
   for (int i = 0; i < SPL_FEAR; ++i)
      spell[i] = o_Spell[i];

   spell[SPL_FEAR].type = -1;
   spell[SPL_FEAR].wav_name = "Fear.wav";
   spell[SPL_FEAR].animation_ix = 15;
   spell[SPL_FEAR].flags = 0x20415;
   spell[SPL_FEAR].name = "Fear";
   spell[SPL_FEAR].short_name = "Fear";
   spell[SPL_FEAR].level = 4;
   spell[SPL_FEAR].school_flags = 8;
   spell[SPL_FEAR].mana_cost[0] = 16;
   spell[SPL_FEAR].mana_cost[1] = 8;
   spell[SPL_FEAR].mana_cost[2] = 8;
   spell[SPL_FEAR].mana_cost[3] = 8;
   spell[SPL_FEAR].eff_power = 0;
   spell[SPL_FEAR].effect[0] = 75;
   spell[SPL_FEAR].effect[1] = 75;
   spell[SPL_FEAR].effect[2] = 50;
   spell[SPL_FEAR].effect[3] = 25;
   spell[SPL_FEAR].chance2get_var[0] = 10;
   spell[SPL_FEAR].chance2get_var[1] = 10;
   spell[SPL_FEAR].chance2get_var[2] = 10;
   spell[SPL_FEAR].chance2get_var[3] = 10;
   spell[SPL_FEAR].chance2get_var[4] = 10;
   spell[SPL_FEAR].chance2get_var[5] = 10;
   spell[SPL_FEAR].chance2get_var[6] = 10;
   spell[SPL_FEAR].chance2get_var[7] = 10;
   spell[SPL_FEAR].chance2get_var[8] = 10;
   spell[SPL_FEAR].ai_value[0] = 50;
   spell[SPL_FEAR].ai_value[1] = 50;
   spell[SPL_FEAR].ai_value[2] = 50;
   spell[SPL_FEAR].ai_value[3] = 50;
   spell[SPL_FEAR].description[0] =
      "{Fear}\n\nStrikes an enemy unit with such fear that it becomes unable to attack and nearly unable to move.\n";
   spell[SPL_FEAR].description[1] =
      "{Basic Fear}\n\nStrikes an enemy unit with such fear that it becomes unable to attack and nearly unable to move.\n\nSpell Point cost is half that of Normal Fear.\n";
   spell[SPL_FEAR].description[2] =
      "{Advanced Fear}\n\nStrikes an enemy unit with such fear that it becomes unable to attack and nearly unable to move.\n\nPenalty to movement is twice that of Basic Fear.\n";
   spell[SPL_FEAR].description[3] =
      "{Expert Fear}\n\nStrikes an enemy unit with such fear that it becomes unable to attack and nearly unable to move.\n\nPenalty to movement is three times that of Basic Fear.\n";

   for (int i = SPL_FEAR + 1; i < SPL_FEAR + 11; ++i)
      spell[i] = o_Spell[i];

   _PI->WriteDword(0x687FA8, (int)&spell);

   return EXEC_DEFAULT;
}

void __fastcall updateSpellsFromArtifacts(_Hero_* hero)
{
   for (int iSpell = 0; iSpell < SPELLS_MAX; ++iSpell)
      if (hero->spell[iSpell] > 1) hero->spell[iSpell] = 0;

   for (int iSlot = 0; iSlot < 19; ++iSlot)
   {
      if (hero->doll_art[iSlot].id != ID_NONE)
      {
         if (hero->doll_art[iSlot].id == AID_SPELL_SCROLL)
         {
            if (hero->doll_art[iSlot].mod < SPELLS_NUM && !hero->spell[hero->doll_art[iSlot].mod])
               hero->spell[hero->doll_art[iSlot].mod] = 2;
         }
         else if (o_ArtInfo[hero->doll_art[iSlot].id].new_spell)
         {
            std::bitset<SPELLS_MAX> spells;
            CALL_2(int, __fastcall, 0x4D95C0, &spells, hero->doll_art[iSlot].id);

            for (std::size_t iSpell = 0; iSpell < spells.size(); ++iSpell)
            {
               if (spells[iSpell] && !hero->spell[iSpell])
                  hero->spell[iSpell] = 2;
            }

            int comboArtIndex = o_ArtInfo[hero->doll_art[iSlot].id].supercomposite;
            if (comboArtIndex != -1)
            {
               for (int iArt = 0; iArt < ARTIFACTS_NUM; ++iArt)
               {
                  if (o_ComboArtInfo[comboArtIndex].parts[iArt] && o_ArtInfo[iArt].new_spell)
                  {
                     std::bitset<SPELLS_MAX> spells;
                     CALL_2(int, __fastcall, 0x4D95C0, &spells, iArt);

                     for (std::size_t iSpell = 0; iSpell < spells.size(); ++iSpell)
                     {
                        if (spells[iSpell] && !hero->spell[iSpell])
                           hero->spell[iSpell] = 2;
                     }
                  }
               }
            }
         }
      }
   }
}

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("HD.Plugin.H3.NewSpells");

         //GetCurrentDirectoryA(sizeof(iniPath), iniPath);
         //strcat(iniPath, "\\NewSpells.ini");

         // Moving and Expanding Spell Table
         _PI->WriteLoHook(0x4EE1C1, afterInit);
               
         // nwcthereisnospoon
         _PI->WriteByte (0x402902, SPELLS_NUM);

         // "(Already learned)" Text
         _PI->WriteDword(0x40D97C, 0x3EA);
         
         // AI
         _PI->WriteDword(0x41FBDF, 0x3EA);
         _PI->WriteByte (0x41FC91, SPELLS_NUM);
         _PI->WriteDword(0x425C72, 0x3EA);
         _PI->WriteDword(0x425E98, SPELLS_NUM * sizeof(_Spell_));

         // Scholar Secondary Skill
         _PI->WriteDword(0x427039, 0x3EA);
         _PI->WriteDword(0x427044, 0x3EA);
         _PI->WriteByte (0x427085, SPELLS_NUM);
         
         _PI->WriteDword(0x4329C1, 0x3EA);
         _PI->WriteDword(0x432A43, SPELLS_NUM * sizeof(_Spell_));
         
         _PI->WriteDword(0x432CDE, 0x3EA);
         _PI->WriteDword(0x432D1E, SPELLS_NUM * sizeof(_Spell_));
         
         // Master Genie AI Spell Weighting
         _PI->WriteDword(0x43C21B, SPELLS_NUM * sizeof(_Spell_));
         
         // Battle AI
         _PI->WriteDword(0x43C6F2, SPELLS_NUM * sizeof(_Spell_));

         // Can cast?
         _PI->WriteDword(0x447551, SPELLS_NUM * sizeof(_Spell_));
         
         _PI->WriteDword(0x447C7D, SPELLS_NUM * sizeof(_Spell_));
         _PI->WriteDword(0x447CC8, SPELLS_NUM * sizeof(_Spell_));
         
         // Cheats in battle
         _PI->WriteByte (0x471C57, SPELLS_NUM);
         
         // Clear _Hero_.spell[140] (optional)
         _PI->WriteDword(0x48647A, 0x23);
         _PI->WriteJmp  (0x486485, 0x486498);
         
         _PI->WriteByte (0x4864B0, SPELLS_NUM);
         
         // Load Game?
         _PI->WriteByte (0x48A34B, SPELLS_NUM);
         
         // New Game?
         _PI->WriteByte (0x4C244D, SPELLS_NUM);
         _PI->WriteByte (0x4C246F, SPELLS_NUM);
         _PI->WriteDword(0x4C24C9, SPELLS_NUM * sizeof(_Spell_));
         _PI->WriteByte (0x4C250E, SPELLS_NUM);
         _PI->WriteDword(0x4C2557, SPELLS_NUM * sizeof(_Spell_));
         _PI->WriteDword(0x4C260D, SPELLS_NUM * sizeof(_Spell_));

         _PI->WriteByte (0x4E67AC, SPELLS_NUM);
         
         // Cheat Menu?
         _PI->WriteByte (0x4F508B, SPELLS_NUM);
         _PI->WriteDword(0x4F50CE, SPELLS_NUM * sizeof(_Spell_));
         _PI->WriteDword(0x4F5114, SPELLS_NUM * sizeof(_Spell_));
                           
         // TODO: _GameMgr_.disabled_spells[SPELLS_NUM]

         // Pyramids
         _PI->WriteByte (0x4C170D, SPELLS_NUM);
                 
         // Shrines
         _PI->WriteDword(0x4C92C5, SPELLS_NUM * sizeof(_Spell_));
         _PI->WriteDword(0x4C9347, SPELLS_NUM * sizeof(_Spell_));
         _PI->WriteDword(0x4C93C0, SPELLS_NUM * sizeof(_Spell_));
         
         _PI->WriteByte (0x4CEC4F, SPELLS_NUM);
         
         // Clear _Hero_.spell[140] (optional)
         _PI->WriteDword(0x4D8F2E, 0x23);
         _PI->WriteJmp  (0x4D8F36, 0x4D8F49);
         
         _PI->WriteByte (0x4D8F53, SPELLS_NUM);
         _PI->WriteByte (0x4D8F8A, SPELLS_NUM);
         
         // Tome of Air Magic
         _PI->WriteDword(0x4D962D, SPELLS_NUM * sizeof(_Spell_));
         // Tome of Fire Magic
         _PI->WriteDword(0x4D9681, SPELLS_NUM * sizeof(_Spell_));
         // Tome of Water Magic
         _PI->WriteDword(0x4D96D6, SPELLS_NUM * sizeof(_Spell_));
         // Tome of Earth Magic
         _PI->WriteDword(0x4D972E, SPELLS_NUM * sizeof(_Spell_));
         // Spellbinder's Hat
         _PI->WriteDword(0x4D9771, SPELLS_NUM * sizeof(_Spell_));
         
         // Scholars
         _PI->WriteByte (0x5012B7, SPELLS_NUM);
         
         _PI->WriteDword(0x527ACB, 0x3EA);
         _PI->WriteDword(0x527B08, SPELLS_NUM * sizeof(_Spell_));

         // AI
         _PI->WriteDword(0x52A97A, 0x3EA);
         _PI->WriteDword(0x52A9B6, SPELLS_NUM * sizeof(_Spell_));

         // RMG
         _PI->WriteDword(0x534C4B, SPELLS_NUM * sizeof(_Spell_));
         
         // RMG Spell Scrolls
         _PI->WriteDword(0x5353D5, SPELLS_NUM);
         _PI->WriteByte (0x53542B, SPELLS_NUM);
         
         // Spell Book
         _PI->WriteDword(0x59CD5E, 0x3EA);
         _PI->WriteDword(0x59CDBF, SPELLS_NUM * sizeof(_Spell_));

         // Cast Spell
         _PI->WriteDword(0x5A1AD6, SPELLS_NUM * sizeof(_Spell_));

         // Mage Guild
         _PI->WriteByte (0x5BEA2C, SPELLS_NUM);
         _PI->WriteByte (0x5BEA70, SPELLS_NUM);
         _PI->WriteByte (0x5BEAAD, SPELLS_NUM);
         _PI->WriteDword(0x5BEAFE, SPELLS_NUM * sizeof(_Spell_));
         _PI->WriteDword(0x5BEB2C, SPELLS_NUM * sizeof(_Spell_));
         _PI->WriteByte (0x5BEB48, SPELLS_NUM);
         _PI->WriteByte (0x5BEB71, SPELLS_NUM);
         _PI->WriteByte (0x5BEBB2, SPELLS_NUM);
         _PI->WriteDword(0x5BEC05, SPELLS_NUM * sizeof(_Spell_));
         _PI->WriteByte (0x5BEC1B, SPELLS_NUM);
                 
         // Conflux Grail
         _PI->WriteDword(0x5BE512, SPELLS_NUM * sizeof(_Spell_));
         _PI->WriteDword(0x5BE56E, SPELLS_NUM * sizeof(_Spell_));
         _PI->WriteDword(0x5D7464, SPELLS_NUM);
         
         // ---------------------------
         // New _Hero_.spell[140] field
         // ---------------------------
         _PI->WriteCodePatch(0x4D8B72, "%n", 8);
         _PI->WriteCodePatch(0x4D8F7F, "%n", 8);
         _PI->WriteCodePatch(0x4D95AF, "%n", 7);
         
         // AI Quick Battle
         _PI->WriteDword(0x433026, 0x3EA);
         _PI->WriteDword(0x433719, 0x3EA);
         _PI->WriteDword(0x43940C, 0x3EA);
         _PI->WriteDword(0x43C561, 0x3EA);
                 
         _PI->WriteDword(0x4E57CC, 0x3EA);
         _PI->WriteDword(0x52AE1C, 0x3EA);

         // Artifacts (incl. Spell Scrolls);
         _PI->WriteHiHook(0x4D9840, SPLICE_, DIRECT_, THISCALL_, updateSpellsFromArtifacts);
      }
   }

   return TRUE;
}


* * *
Подумал, что таблицу заклинаний существ нужно в самый конец двигать, чтобы без проблем можно было добавлять новые заклы. Или почти в самый, чтобы осталось место для новых заклинаний существ. 128 - чисто техническое ограничение (на всякий). В коде, скорее всего, есть инструкции, которые работают со знаковым байтом. Но их тоже можно найти и легко переписать на беззнаковые. Тогда получится 256 заклинаний, но что-то я сомневаюсь, что кто-то напишет хотя бы 10. Естественно, можно переделать всякие огненные шары и инферно, чтобы они, например, наносили урон другой стихией. Но реально интересных 10 вряд ли получим :smile1: И кому даже тех 10 писать? Программерам только. Какими бы ни были крутыми скрипты, добавить код заклинания с помощью них практически нереально. Разве создать какой-то шаблон заклинания с настраиваемыми параметрами. Интересно, в Era что-то подобное есть?
Вернуться к началу

onlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5538
Зарегистрирован: 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)
Поблагодарили: 2155 раз.

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

Сообщение AlexSpl » 20 авг 2021, 10:02

Добавил возможность каста новых заклинаний (у нас пока только одно - Fear):

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

#define SPELLS_MAX 128
#define ANIMS_MAX 128
#define ANIMS_NUM 84
#define ANIM_FEAR 83
#define ARTIFACTS_NUM 144
#define SPELLS_NUM 71
#define SPECABIL_NUM 11
#define SPL_FEAR 70

Patcher* _P;
PatcherInstance* _PI;

struct _ComboArtInfo_ {
   int index;
   std::bitset<160> parts;
};

struct _MagicAnim_ {
   char* defName;
   char* name;
   int type;
};

#define o_ComboArtInfo (*(_ComboArtInfo_**)0x660B6C)
#define o_MagicAnim ((_MagicAnim_*)0x641E18)

_Spell_ spell[SPELLS_MAX];
_MagicAnim_ anim[ANIMS_MAX];
//char iniPath[MAX_PATH];

char spellIndirectTableA[] = {
   // Spells
   0, 1, 2, 2, 3, 4, 4, 4, 4, 4,
   5, 5, 5, 5, 4, 4, 4, 4, 4, 4,
   4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
   6, 4, 4, 4, 4, 4, 4, 4, 4, 4,
   4, 4, 4, 4, 4, 4, 4, 4, 4, 5,
   4, 4, 4, 7, 8, 9, 10, 10, 10, 10,
   4 // <- Fear
};

char spellIndirectTableB[] = {
   // Spells
   0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
   10, 11, 12, 13, 14, 15, 16, 17, 17, 17,
   17, 17, 17, 17, 17, 18, 17, 19, 20, 20,
   21, 17, 17, 22, 17, 17, 17, 23, 17, 17,
   17, 17, 17, 17, 17, 17, 17, 7, 17, 24,
   17, 17, 17, 25, 26, 27, 28, 29, 30, 31,
   17, // <- Fear
   // Special Abilities
   32, 33, 34, 17, 17, 34, 37, 37, 35, 37, 36
};

int __stdcall afterInit(LoHook* h, HookContext* c)
{
   // Spell Table
   for (int i = 0; i < SPL_FEAR; ++i)
      spell[i] = o_Spell[i];

   spell[SPL_FEAR].type = -1;
   spell[SPL_FEAR].wav_name = "Fear.wav";
   spell[SPL_FEAR].animation_ix = ANIM_FEAR;
   spell[SPL_FEAR].flags = 0x20415;
   spell[SPL_FEAR].name = "Fear";
   spell[SPL_FEAR].short_name = "Fear";
   spell[SPL_FEAR].level = 4;
   spell[SPL_FEAR].school_flags = 8;
   spell[SPL_FEAR].mana_cost[0] = 16;
   spell[SPL_FEAR].mana_cost[1] = 8;
   spell[SPL_FEAR].mana_cost[2] = 8;
   spell[SPL_FEAR].mana_cost[3] = 8;
   spell[SPL_FEAR].eff_power = 0;
   spell[SPL_FEAR].effect[0] = 75;
   spell[SPL_FEAR].effect[1] = 75;
   spell[SPL_FEAR].effect[2] = 50;
   spell[SPL_FEAR].effect[3] = 25;
   spell[SPL_FEAR].chance2get_var[0] = 10;
   spell[SPL_FEAR].chance2get_var[1] = 10;
   spell[SPL_FEAR].chance2get_var[2] = 10;
   spell[SPL_FEAR].chance2get_var[3] = 10;
   spell[SPL_FEAR].chance2get_var[4] = 10;
   spell[SPL_FEAR].chance2get_var[5] = 10;
   spell[SPL_FEAR].chance2get_var[6] = 10;
   spell[SPL_FEAR].chance2get_var[7] = 10;
   spell[SPL_FEAR].chance2get_var[8] = 10;
   spell[SPL_FEAR].ai_value[0] = 50;
   spell[SPL_FEAR].ai_value[1] = 50;
   spell[SPL_FEAR].ai_value[2] = 50;
   spell[SPL_FEAR].ai_value[3] = 50;
   spell[SPL_FEAR].description[0] =
      "{Fear}\n\nStrikes an enemy unit with such fear that it becomes unable to attack and nearly unable to move.\n";
   spell[SPL_FEAR].description[1] =
      "{Basic Fear}\n\nStrikes an enemy unit with such fear that it becomes unable to attack and nearly unable to move.\n\nSpell Point cost is half that of Normal Fear.\n";
   spell[SPL_FEAR].description[2] =
      "{Advanced Fear}\n\nStrikes an enemy unit with such fear that it becomes unable to attack and nearly unable to move.\n\nPenalty to movement is twice that of Basic Fear.\n";
   spell[SPL_FEAR].description[3] =
      "{Expert Fear}\n\nStrikes an enemy unit with such fear that it becomes unable to attack and nearly unable to move.\n\nPenalty to movement is three times that of Basic Fear.\n";

   for (int i = SPL_FEAR + 1; i < SPL_FEAR + 11; ++i)
      spell[i] = o_Spell[i];

   _PI->WriteDword(0x687FA8, (int)&spell);

   // Magic Animation Table
   for (int i = 0; i < ANIM_FEAR; ++i)
      anim[i] = o_MagicAnim[i];

   anim[ANIM_FEAR].defName = "C07spE0.def";
   anim[ANIM_FEAR].name = "Fear";
   anim[ANIM_FEAR].type = 0x101;

   int AnimAddrDefName[] = {0x43F77E, 0x43FB6A, 0x4963FB, 0x4965CF, 0x5A5036, 0x5A6B14, 0x5A7A74, 0x5A962C};
   int AnimAddrName[] = {0x4689C4, 0x49651A, 0x4966CD, 0x5A6D2D, 0x5A7B06};
   
   for (int i = 0; i < sizeof(AnimAddrDefName) / sizeof(int); ++i)
      _PI->WriteDword(AnimAddrDefName[i], (int)&anim->defName);

   for (int i = 0; i < sizeof(AnimAddrName) / sizeof(int); ++i)
      _PI->WriteDword(AnimAddrName[i], (int)&anim->name);

   _PI->WriteDword(0x43E503, (int)&anim->type);
   
   return EXEC_DEFAULT;
}

void __fastcall updateSpellsFromArtifacts(_Hero_* hero)
{
   for (int iSpell = 0; iSpell < SPELLS_MAX; ++iSpell)
      if (hero->spell[iSpell] > 1) hero->spell[iSpell] = 0;

   for (int iSlot = 0; iSlot < 19; ++iSlot)
   {
      if (hero->doll_art[iSlot].id != ID_NONE)
      {
         if (hero->doll_art[iSlot].id == AID_SPELL_SCROLL)
         {
            if (hero->doll_art[iSlot].mod < SPELLS_NUM && !hero->spell[hero->doll_art[iSlot].mod])
               hero->spell[hero->doll_art[iSlot].mod] = 2;
         }
         else if (o_ArtInfo[hero->doll_art[iSlot].id].new_spell)
         {
            std::bitset<SPELLS_MAX> spells;
            CALL_2(int, __fastcall, 0x4D95C0, &spells, hero->doll_art[iSlot].id);

            for (std::size_t iSpell = 0; iSpell < spells.size(); ++iSpell)
            {
               if (spells[iSpell] && !hero->spell[iSpell])
                  hero->spell[iSpell] = 2;
            }

            int comboArtIndex = o_ArtInfo[hero->doll_art[iSlot].id].supercomposite;
            if (comboArtIndex != -1)
            {
               for (int iArt = 0; iArt < ARTIFACTS_NUM; ++iArt)
               {
                  if (o_ComboArtInfo[comboArtIndex].parts[iArt] && o_ArtInfo[iArt].new_spell)
                  {
                     std::bitset<SPELLS_MAX> spells;
                     CALL_2(int, __fastcall, 0x4D95C0, &spells, iArt);

                     for (std::size_t iSpell = 0; iSpell < spells.size(); ++iSpell)
                     {
                        if (spells[iSpell] && !hero->spell[iSpell])
                           hero->spell[iSpell] = 2;
                     }
                  }
               }
            }
         }
      }
   }
}

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("HD.Plugin.H3.NewSpells");

         //GetCurrentDirectoryA(sizeof(iniPath), iniPath);
         //strcat(iniPath, "\\NewSpells.ini");

         // Moving and Expanding Spell Table
         _PI->WriteLoHook(0x4EE1C1, afterInit);
               
         // nwcthereisnospoon
         _PI->WriteByte (0x402902, SPELLS_NUM);

         // "(Already learned)" Text
         _PI->WriteDword(0x40D97C, 0x3EA);
         
         // AI
         _PI->WriteDword(0x41FBDF, 0x3EA);
         _PI->WriteByte (0x41FC91, SPELLS_NUM);
         _PI->WriteDword(0x425C72, 0x3EA);
         _PI->WriteDword(0x425E98, SPELLS_NUM * sizeof(_Spell_));

         // Scholar Secondary Skill
         _PI->WriteDword(0x427039, 0x3EA);
         _PI->WriteDword(0x427044, 0x3EA);
         _PI->WriteByte (0x427085, SPELLS_NUM);
         
         _PI->WriteDword(0x4329C1, 0x3EA);
         _PI->WriteDword(0x432A43, SPELLS_NUM * sizeof(_Spell_));
         
         _PI->WriteDword(0x432CDE, 0x3EA);
         _PI->WriteDword(0x432D1E, SPELLS_NUM * sizeof(_Spell_));
         
         // Master Genie AI Spell Weighting
         _PI->WriteDword(0x43C21B, SPELLS_NUM * sizeof(_Spell_));
         
         // Battle AI
         _PI->WriteDword(0x43C6F2, SPELLS_NUM * sizeof(_Spell_));

         // Can cast?
         _PI->WriteDword(0x447551, SPELLS_NUM * sizeof(_Spell_));
         
         _PI->WriteDword(0x447C7D, SPELLS_NUM * sizeof(_Spell_));
         _PI->WriteDword(0x447CC8, SPELLS_NUM * sizeof(_Spell_));
         
         // Cheats in battle
         _PI->WriteByte (0x471C57, SPELLS_NUM);
         
         // Clear _Hero_.spell[140] (optional)
         _PI->WriteDword(0x48647A, 0x23);
         _PI->WriteJmp  (0x486485, 0x486498);
         
         _PI->WriteByte (0x4864B0, SPELLS_NUM);
         
         // Load Game?
         _PI->WriteByte (0x48A34B, SPELLS_NUM);
         
         // New Game?
         _PI->WriteByte (0x4C244D, SPELLS_NUM);
         _PI->WriteByte (0x4C246F, SPELLS_NUM);
         _PI->WriteDword(0x4C24C9, SPELLS_NUM * sizeof(_Spell_));
         _PI->WriteByte (0x4C250E, SPELLS_NUM);
         _PI->WriteDword(0x4C2557, SPELLS_NUM * sizeof(_Spell_));
         _PI->WriteDword(0x4C260D, SPELLS_NUM * sizeof(_Spell_));

         _PI->WriteByte (0x4E67AC, SPELLS_NUM);
         
         // Cheat Menu?
         _PI->WriteByte (0x4F508B, SPELLS_NUM);
         _PI->WriteDword(0x4F50CE, SPELLS_NUM * sizeof(_Spell_));
         _PI->WriteDword(0x4F5114, SPELLS_NUM * sizeof(_Spell_));
                           
         // TODO: _GameMgr_.disabled_spells[SPELLS_NUM]

         // Pyramids
         _PI->WriteByte (0x4C170D, SPELLS_NUM);
                 
         // Shrines
         _PI->WriteDword(0x4C92C5, SPELLS_NUM * sizeof(_Spell_));
         _PI->WriteDword(0x4C9347, SPELLS_NUM * sizeof(_Spell_));
         _PI->WriteDword(0x4C93C0, SPELLS_NUM * sizeof(_Spell_));
         
         _PI->WriteByte (0x4CEC4F, SPELLS_NUM);
         
         // Clear _Hero_.spell[140] (optional)
         _PI->WriteDword(0x4D8F2E, 0x23);
         _PI->WriteJmp  (0x4D8F36, 0x4D8F49);
         
         _PI->WriteByte (0x4D8F53, SPELLS_NUM);
         _PI->WriteByte (0x4D8F8A, SPELLS_NUM);
         
         // Tome of Air Magic
         _PI->WriteDword(0x4D962D, SPELLS_NUM * sizeof(_Spell_));
         // Tome of Fire Magic
         _PI->WriteDword(0x4D9681, SPELLS_NUM * sizeof(_Spell_));
         // Tome of Water Magic
         _PI->WriteDword(0x4D96D6, SPELLS_NUM * sizeof(_Spell_));
         // Tome of Earth Magic
         _PI->WriteDword(0x4D972E, SPELLS_NUM * sizeof(_Spell_));
         // Spellbinder's Hat
         _PI->WriteDword(0x4D9771, SPELLS_NUM * sizeof(_Spell_));
         
         // Scholars
         _PI->WriteByte (0x5012B7, SPELLS_NUM);
         
         _PI->WriteDword(0x527ACB, 0x3EA);
         _PI->WriteDword(0x527B08, SPELLS_NUM * sizeof(_Spell_));

         // AI
         _PI->WriteDword(0x52A97A, 0x3EA);
         _PI->WriteDword(0x52A9B6, SPELLS_NUM * sizeof(_Spell_));

         // RMG
         _PI->WriteDword(0x534C4B, SPELLS_NUM * sizeof(_Spell_));
         
         // RMG Spell Scrolls
         _PI->WriteDword(0x5353D5, SPELLS_NUM);
         _PI->WriteByte (0x53542B, SPELLS_NUM);
         
         // Spell Book
         _PI->WriteDword(0x59CD5E, 0x3EA);
         _PI->WriteDword(0x59CDBF, SPELLS_NUM * sizeof(_Spell_));

         // Cast Spell
         _PI->WriteDword(0x5A1AD6, SPELLS_NUM * sizeof(_Spell_));

         // Mage Guild
         _PI->WriteByte (0x5BEA2C, SPELLS_NUM);
         _PI->WriteByte (0x5BEA70, SPELLS_NUM);
         _PI->WriteByte (0x5BEAAD, SPELLS_NUM);
         _PI->WriteDword(0x5BEAFE, SPELLS_NUM * sizeof(_Spell_));
         _PI->WriteDword(0x5BEB2C, SPELLS_NUM * sizeof(_Spell_));
         _PI->WriteByte (0x5BEB48, SPELLS_NUM);
         _PI->WriteByte (0x5BEB71, SPELLS_NUM);
         _PI->WriteByte (0x5BEBB2, SPELLS_NUM);
         _PI->WriteDword(0x5BEC05, SPELLS_NUM * sizeof(_Spell_));
         _PI->WriteByte (0x5BEC1B, SPELLS_NUM);
                 
         // Conflux Grail
         _PI->WriteDword(0x5BE512, SPELLS_NUM * sizeof(_Spell_));
         _PI->WriteDword(0x5BE56E, SPELLS_NUM * sizeof(_Spell_));
         _PI->WriteDword(0x5D7464, SPELLS_NUM);
         
         // ---------------------------
         // New _Hero_.spell[140] field
         // ---------------------------
         _PI->WriteCodePatch(0x4D8B72, "%n", 8);
         _PI->WriteCodePatch(0x4D8F7F, "%n", 8);
         _PI->WriteCodePatch(0x4D95AF, "%n", 7);
         
         // AI Quick Battle
         _PI->WriteDword(0x433026, 0x3EA);
         _PI->WriteDword(0x433719, 0x3EA);
         _PI->WriteDword(0x43940C, 0x3EA);
         _PI->WriteDword(0x43C561, 0x3EA);
                 
         _PI->WriteDword(0x4E57CC, 0x3EA);
         _PI->WriteDword(0x52AE1C, 0x3EA);

         // Combat Batyanya
         _PI->WriteByte (0x59EFD9, SPELLS_NUM - 1 - 0xA);
         _PI->WriteDword(0x59EFE4, (int)&spellIndirectTableA);
         // 70 is probably a bug, should have been SPELLS_NUM + SPECABIL_NUM - 1 - 0xA
         // _PI->WriteByte  (0x5A064E, SPELLS_NUM + SPECABIL_NUM - 1 - 0xA);
         _PI->WriteDword(0x5A0659, (int)&spellIndirectTableB);
         _PI->WriteByte (0x4963E9, ANIMS_NUM);

         // Artifacts (incl. Spell Scrolls);
         _PI->WriteHiHook(0x4D9840, SPLICE_, DIRECT_, THISCALL_, updateSpellsFromArtifacts);
      }
   }

   return TRUE;
}

Картинку заклинания для окна информации о существе добавлю позже. Так как таблицу спецабилок не двигали, то Fear работает, как окаменение (это даже хорошо, что работает, пока нет собственного кода).

Кстати, можно добавить все спецабилки с анимацией в качестве магии, если очень хочется. Для них только картинок нет (для гильдии магов и книги заклинаний).

* * *
Придумал, как решить проблему _GameMgr_.disabled_spells[SPELLS_NUM]: точно таким же способом, как и с полем _Hero_.spell[SPELLS_NUM]. Дело в том, что disabled_shrines[70] - это вовсе не "запрещённые святилища", в этом массиве хранятся заклинания, попавшие в святилища на карте, а также запрещённые. Массив служит для того, чтобы эти заклинания не дублировались. Только где? Хороший вопрос :smile2: Нигде. Поставьте на карту 20 святилищ 1-го уровня и в них уже будут дубликаты заклинаний. Так что мы законно забираем это поле под disabled_spells[].

Вы можете посмотреть на цикл 0x4C92EF. Из него видно, что поле disabled_shrines[70] используется при генерации карты, чтобы как можно дольше не генерировать дубликаты заклинаний в святилищах. Но после того, как вся работа сделана, мы можем спокойно хранить там запрещённые заклинания, т.к. в процессе игры обращений к этому массиву не происходит.
Вернуться к началу

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

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

Сообщение Rolex » 20 авг 2021, 14:42

Закончил с картинками для заклов-абилок: Aging (Старение), Poison (Яд), Disease (Болезнь).

Проапскейлил оригинальные картинки абилок на 60-70% почти без потерь в качестве.

Работа велась в 3 этапа: выкроил картинку из фона, увеличил через ИИ, и после попиксельная шлифовка полученного изображения в Фотошопе.

В этом деле очень сильно помогли современные алгоритмы масштабирования изображения с помощью ИИ:
https://www.img2go.com/upscale-image

Пример:
Оригинальная картинка Aging (Старения):
Изображение

Простой апскейл в Фотошопе:
Изображение

Апскейл через ИИ + доп обработка в Фотошопе:
Изображение
---
Болезнь (Disease):
Изображение
Изображение
---
Яд (Poison):
Изображение
Изображение

---

Изображение

Изображение

Изображение

Описание работы заклов писал ЗДЕСЬ. Кстати, надо бы сделать, чтобы Dispel и Cure снимали Яд и восстанавливали прежнее здоровье отряда существ. Со Старением все ок, здоровье восстанавливается после диспела и лечения.

Яд пускай в течении 3 раундов накапливает процент (+10%/+13,33%/+16,66% в зависимости от уровня школы), анимация Яда перед каждым ходом, пока снижается здоровье. После 3-го хода анимацию Яда уже не воспроизводим, но здоровье остается сниженным и Яд действует кол-во раундов соотвующее СМ героя, либо пока не будет снят Dispel или Cure.

Если СМ героя меньше 3, то тогда Яд даже не достигнет своего макс эффекта. Старение сразу дает свой макс эффект, а Яду нужно 3 раунда для достижения своего макс эффекта. Именно по этой причине Яд делаем заклом 4-го уровня, а Старение - 5-го.

Все 8-битные bmp-шки для DefTool в архиве. Плюс там еще Fear, подправил там его еще немного.
Вложения
Images.rar
(99.2 КБ) Скачиваний: 88
Последний раз редактировалось Rolex 20 авг 2021, 15:15, всего редактировалось 8 раз(а).
Вернуться к началу

onlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5538
Зарегистрирован: 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)
Поблагодарили: 2155 раз.

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

Сообщение AlexSpl » 20 авг 2021, 14:45

Отлично получилось. Постараюсь добавить сегодня-завтра. Будет 3 полноценных* новых закла и 1 в процессе. Только заполните структуры _Spell_ для них (те, что можете).

* Хотя поспешил. Они же от СМ не зависят. Придётся править код.

Цитата:
Плюс там еще Fear, подправил там его еще немного.

"Маленький" Fear есть в ресурсах RoE. Я оттуда его возьму.

* * *
Цитата:
Так что мы законно забираем это поле под disabled_spells[].

Вот же... Есть нюанс. Массив disabled_shrines[70] вряд ли идёт в сейв. Но что-нибудь придумать можно.
Последний раз редактировалось AlexSpl 20 авг 2021, 14:58, всего редактировалось 1 раз.
Вернуться к началу

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

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

Сообщение Rolex » 20 авг 2021, 14:57

AlexSpl писал(а):

* Хотя поспешил. Они же от СМ не зависят. Придётся править код.

Да, там 3 раунда нужно заменить везде на СМ героя. Плюс на Базовый/Продвинутый/Экспертный свой процент. К Болезни хотелось бы добавить еще эффект Замедления, чтобы получилась противоположность Молитве. И Яд нужно сделать, чтобы снимался диспелом и лечением восстанавливая прежнее Здоровье отряда существ, и там также свои проценты в зависмоти от уровня развития школы.

Подробней писал здесь.
Вернуться к началу

onlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5538
Зарегистрирован: 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)
Поблагодарили: 2155 раз.

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

Сообщение AlexSpl » 20 авг 2021, 15:02

Сейчас остаются задачи (самые важные, без которых плагин не будет полноценным):
1) Расширение поля disabled_spells[70], чтобы оно не налезало на критикал секшн bink;
2) Уточнение замены поля +430h на +3EAh в некоторых случаях (например, герой со вторичным навыком Scholar не должен учить другого героя заклинаниям из свитков);
3) Функции оценки новых заклинаний AI.
Вернуться к началу

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

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

Сообщение Rolex » 20 авг 2021, 17:02

AlexSpl писал(а):

Добавил возможность каста новых заклинаний (у нас пока только одно - Fear)

Кстати, анимация Смертельного взгляда Могучих горгон, которая также соответствует и заклу Fear у Могучих горгон сопровождается другим звуком, и это не Fear.wav.
Звук Fear.wav, которым сопровожается сейчас наш закл Fear проигрывается также и во время анимации Страха Лазурных драконов. Вот мне стало интересно, а со звуком мы угадали? У нас это звук Страха Лазурных драконов. Так вот, так и нужно или все же правильный звук, это звук, который идет вместе с анимацией Могучих горгон?
Вернуться к началу

Пред.След.

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

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

Сейчас этот форум просматривают: GoGo.Ru [Bot] и гости: 5