Объявления

Друзья, если не получается зарегистрироваться, напишите на почту 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 » 20 авг 2021, 20:29

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

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

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

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

AlexSpl писал(а):

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

Тогда предлагаю поступить следующий образом. В папку с dll плагином мы помещаем наш lod-архив, где будут наши def-ки с картинками заклов, оригинальный звук Fear из RoE, а также 2 текстовых файла Eng и Rus с описанием наших заклов. Плюс с dll и lod будет ini-файл. В ini пользователь сможет задавать язык на котором он хочет, чтобы было описание заклинаний, а также выбирать заклинания, которые он хочет, чтобы работали.

Например, если задан Language = 0, то отображаем описание заклов на анг языке, если Language = 1, то на русском. Fear = 0 - не добавляет в гильдию, книгу, свитки, ученых, как будто его нет, просто полностью откл его. Fear = 1 - полностью вкл данный закл, отображаем везде и тд. Аналогично и с другим заклами. Таким образом игрок сможет выбрать и подключить себе те заклы, которые ему больше всего по душе.

Наш lod мы подгружаем вместе с dll и подменяем оригинальные ресурсы с таким же именем в оригинальных лодах. Подобное сделано в SoD_SP. Но там еще менюшка с настройками прямо из игры вызывается и каждую опцию можно изменить прямо в игре через кастомное меню опций с автосохранением всех изменений в ini.
Нам же, думаю, возится с кастомными диалогами и ненужно, достаточно, чтобы пользователь задавал все сам ручками через ini.
Вернуться к началу

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

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

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

Пересмотрел абилки и сделал еще для парочки спецабилок картинки. Также переделал картинки для Disease (Болезнь). Из тех трех, которые я бросал именно Disease получилась хуже всего, две другие, вроде, норм. Так вот в этот раз для Disease выдрал картинку из анимации закла. Она там в очень хорошем качестве и разрешении. Вот ее и взял. Также смотрел bmp-шки из def-ок Aging и Poison, так вот они там не очень (речь не за качество), как для картинок для заклов как-то не очень смотрятся, только для анимации и годятся. Но они нам и ненужны. Из картинок окна информации получилось вполне себе ничего. А вот Disease можно и заменить. Вот она точно лучше.
Касательно новых заклов, то решил, что можно добавить Облако смерти (Death Cloud) и Смертельный удар (Death Blow). Оба закла будут 5-го уровня.

Изображение Изображение Изображение

Изображение Изображение Изображение

Изображение Изображение

1) Death Cloud - площадный закл. У Death Cloud диаметр атаки 3 гекса, как и у Огненного шара. Кстати, Огненный шар идет как закл 3-го уровня, так и как абилка Магогов. Анимация одна и таже. А вот закла Death Cloud нет, только абилка. На 4 уровне есть Метеоритный дождь, а потому решил делать его заклом 5-го уровня с уроном где-то между Метеоритным дождем и Взрывом.

Предлагаю следующий урон:
Базовый: СМ * 40 + 30
Продвинутый: СМ * 40 + 60
Экспертный: СМ * 40 + 120

2) Death Blow будет наподобие Бешенства, только без снижения защиты. То есть Бешенство поднимает атаку врага в 2/2,5/3 раза и урон на 50%/75%/100%, но это все в ущерб защите. А вот в случае со Смертельным ударом защита остается прежней, а потому его и делаем заклом 5-го уровня.

Касательно длительности действия, также как и Бешенство в течении 1 раунда. То есть у дружественного отряда с наложенным Death Blow будет возможность сделать одну усиленную атаку и дать одну усиленную ответку и все это без ущерба к защите.

Урон предлагаю сделать следующий:
Базовый: базовый урон повышается на 50%
Продвинутый: базовый урон повышается на 75%
Экспертный: базовый урон повышается на 100%

Касательно анимации, то здесь, я думаю, лучше проигрывать ее дважды. Один раз при наложении на своем отряде и второй раз перед атакой (или ответкой) уже на вражеском отряде, собственно так, как она и работает у Рыцарей смерти. То есть наподобие Огненного щита, где как при наложении, так и в работе используется одна и таже анимация.

В итоге включая Fear у нас получится 6 заклов: 3 закла 4-го уровня включая Fear (Страх, Яд, Болезнь) и 3 закла 5-го уровня (Старение, Облако смерти, Смертельный удар).

Прикрепил архив с bmp-шками для DefTool:
Вложения
NewImages.rar
(157.48 КБ) Скачиваний: 153
Вернуться к началу

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 » 21 авг 2021, 23:02

Эффекты не добавлял (возможны эффекты спецабилок существ). Версия для демонстации появления в гильдии магии, объектах на карте, изучения героем и каста (анимация/звук/картинка в информационном окошке отряда):

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

#define SPELLS_MAX 128
#define ANIMS_MAX 128
#define ARTIFACTS_NUM 144

#define SPELLS_NUM 76
#define SPECABIL_NUM 11
#define SPL_FEAR 70
#define SPL_POISON 71
#define SPL_DISEASE 72
#define SPL_AGING 73
#define SPL_DEATH_CLOUD 74
#define SPL_DEATH_BLOW 75

#define ANIMS_NUM 89
#define ANIM_FEAR 83
#define ANIM_POISON 84
#define ANIM_DISEASE 85
#define ANIM_AGING 86
#define ANIM_DEATH_CLOUD 87
#define ANIM_DEATH_BLOW 88

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, 4, 4, 4, 5, 4
};

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, 17, 17, 17, 11, 17,
   // 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];

   // Fear
   spell[SPL_FEAR].type = -1;
   spell[SPL_FEAR].wav_name = "FearRoE.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";

   // Poison
   spell[SPL_POISON].type = -1;
   spell[SPL_POISON].wav_name = "Poison.wav";
   spell[SPL_POISON].animation_ix = ANIM_POISON;
   spell[SPL_POISON].flags = 0x1015;
   spell[SPL_POISON].name = "Poison";
   spell[SPL_POISON].short_name = "Poison";
   spell[SPL_POISON].level = 4;
   spell[SPL_POISON].school_flags = 8;
   spell[SPL_POISON].mana_cost[0] = 20;
   spell[SPL_POISON].mana_cost[1] = 16;
   spell[SPL_POISON].mana_cost[2] = 16;
   spell[SPL_POISON].mana_cost[3] = 16;
   spell[SPL_POISON].eff_power = 0;
   spell[SPL_POISON].effect[0] = 0;
   spell[SPL_POISON].effect[1] = 0;
   spell[SPL_POISON].effect[2] = 0;
   spell[SPL_POISON].effect[3] = 0;
   spell[SPL_POISON].chance2get_var[0] = 12;
   spell[SPL_POISON].chance2get_var[1] = 13;
   spell[SPL_POISON].chance2get_var[2] = 12;
   spell[SPL_POISON].chance2get_var[3] = 13;
   spell[SPL_POISON].chance2get_var[4] = 12;
   spell[SPL_POISON].chance2get_var[5] = 13;
   spell[SPL_POISON].chance2get_var[6] = 12;
   spell[SPL_POISON].chance2get_var[7] = 13;
   spell[SPL_POISON].chance2get_var[8] = 13;
   spell[SPL_POISON].ai_value[0] = 50;
   spell[SPL_POISON].ai_value[1] = 50;
   spell[SPL_POISON].ai_value[2] = 50;
   spell[SPL_POISON].ai_value[3] = 50;
   spell[SPL_POISON].description[0] =
      "{Poison}\n\nPoison Description.\n";
   spell[SPL_POISON].description[1] =
      "{Basic Poison}\n\nBasic Poison Description.\n";
   spell[SPL_POISON].description[2] =
      "{Advanced Poison}\n\nAdvanced Poison Description.\n";
   spell[SPL_POISON].description[3] =
      "{Expert Poison}\n\nExpert Poison Description.\n";

   // Desease
   spell[SPL_DISEASE].type = -1;
   spell[SPL_DISEASE].wav_name = "Disease.wav";
   spell[SPL_DISEASE].animation_ix = ANIM_DISEASE;
   spell[SPL_DISEASE].flags = 0x1015;
   spell[SPL_DISEASE].name = "Disease";
   spell[SPL_DISEASE].short_name = "Disease";
   spell[SPL_DISEASE].level = 2;
   spell[SPL_DISEASE].school_flags = 8;
   spell[SPL_DISEASE].mana_cost[0] = 16;
   spell[SPL_DISEASE].mana_cost[1] = 12;
   spell[SPL_DISEASE].mana_cost[2] = 12;
   spell[SPL_DISEASE].mana_cost[3] = 12;
   spell[SPL_DISEASE].eff_power = 0;
   spell[SPL_DISEASE].effect[0] = 0;
   spell[SPL_DISEASE].effect[1] = 0;
   spell[SPL_DISEASE].effect[2] = 0;
   spell[SPL_DISEASE].effect[3] = 0;
   spell[SPL_DISEASE].chance2get_var[0] = 12;
   spell[SPL_DISEASE].chance2get_var[1] = 13;
   spell[SPL_DISEASE].chance2get_var[2] = 12;
   spell[SPL_DISEASE].chance2get_var[3] = 13;
   spell[SPL_DISEASE].chance2get_var[4] = 12;
   spell[SPL_DISEASE].chance2get_var[5] = 13;
   spell[SPL_DISEASE].chance2get_var[6] = 12;
   spell[SPL_DISEASE].chance2get_var[7] = 13;
   spell[SPL_DISEASE].chance2get_var[8] = 13;
   spell[SPL_DISEASE].ai_value[0] = 50;
   spell[SPL_DISEASE].ai_value[1] = 50;
   spell[SPL_DISEASE].ai_value[2] = 50;
   spell[SPL_DISEASE].ai_value[3] = 50;
   spell[SPL_DISEASE].description[0] =
      "{Disease}\n\nDisease Description.\n";
   spell[SPL_DISEASE].description[1] =
      "{Basic Disease}\n\nBasic Disease Description.\n";
   spell[SPL_DISEASE].description[2] =
      "{Advanced Disease}\n\nAdvanced Disease Description.\n";
   spell[SPL_DISEASE].description[3] =
      "{Expert Disease}\n\nExpert Disease Description.\n";

   // Aging
   spell[SPL_AGING].type = -1;
   spell[SPL_AGING].wav_name = "Age.wav";
   spell[SPL_AGING].animation_ix = ANIM_AGING;
   spell[SPL_AGING].flags = 0x1015;
   spell[SPL_AGING].name = "Aging";
   spell[SPL_AGING].short_name = "Aging";
   spell[SPL_AGING].level = 5;
   spell[SPL_AGING].school_flags = 2;
   spell[SPL_AGING].mana_cost[0] = 25;
   spell[SPL_AGING].mana_cost[1] = 20;
   spell[SPL_AGING].mana_cost[2] = 20;
   spell[SPL_AGING].mana_cost[3] = 20;
   spell[SPL_AGING].eff_power = 0;
   spell[SPL_AGING].effect[0] = 0;
   spell[SPL_AGING].effect[1] = 0;
   spell[SPL_AGING].effect[2] = 0;
   spell[SPL_AGING].effect[3] = 0;
   spell[SPL_AGING].chance2get_var[0] = 12;
   spell[SPL_AGING].chance2get_var[1] = 13;
   spell[SPL_AGING].chance2get_var[2] = 12;
   spell[SPL_AGING].chance2get_var[3] = 13;
   spell[SPL_AGING].chance2get_var[4] = 12;
   spell[SPL_AGING].chance2get_var[5] = 13;
   spell[SPL_AGING].chance2get_var[6] = 12;
   spell[SPL_AGING].chance2get_var[7] = 13;
   spell[SPL_AGING].chance2get_var[8] = 13;
   spell[SPL_AGING].ai_value[0] = 50;
   spell[SPL_AGING].ai_value[1] = 50;
   spell[SPL_AGING].ai_value[2] = 50;
   spell[SPL_AGING].ai_value[3] = 50;
   spell[SPL_AGING].description[0] =
      "{Aging}\n\nAging Description.\n";
   spell[SPL_AGING].description[1] =
      "{Basic Aging}\n\nBasic Aging Description.\n";
   spell[SPL_AGING].description[2] =
      "{Advanced Aging}\n\nAdvanced Aging Description.\n";
   spell[SPL_AGING].description[3] =
      "{Expert Aging}\n\nExpert Aging Description.\n";

   // Death Cloud
   spell[SPL_DEATH_CLOUD].type = 0;
   spell[SPL_DEATH_CLOUD].wav_name = "Deathcld.wav";
   spell[SPL_DEATH_CLOUD].animation_ix = ANIM_DEATH_CLOUD;
   spell[SPL_DEATH_CLOUD].flags = 0x8281;
   spell[SPL_DEATH_CLOUD].name = "Death Cloud";
   spell[SPL_DEATH_CLOUD].short_name = "Death Cloud";
   spell[SPL_DEATH_CLOUD].level = 5;
   spell[SPL_DEATH_CLOUD].school_flags = 8;
   spell[SPL_DEATH_CLOUD].mana_cost[0] = 25;
   spell[SPL_DEATH_CLOUD].mana_cost[1] = 20;
   spell[SPL_DEATH_CLOUD].mana_cost[2] = 20;
   spell[SPL_DEATH_CLOUD].mana_cost[3] = 20;
   spell[SPL_DEATH_CLOUD].eff_power = 0;
   spell[SPL_DEATH_CLOUD].effect[0] = 0;
   spell[SPL_DEATH_CLOUD].effect[1] = 0;
   spell[SPL_DEATH_CLOUD].effect[2] = 0;
   spell[SPL_DEATH_CLOUD].effect[3] = 0;
   spell[SPL_DEATH_CLOUD].chance2get_var[0] = 12;
   spell[SPL_DEATH_CLOUD].chance2get_var[1] = 13;
   spell[SPL_DEATH_CLOUD].chance2get_var[2] = 12;
   spell[SPL_DEATH_CLOUD].chance2get_var[3] = 13;
   spell[SPL_DEATH_CLOUD].chance2get_var[4] = 12;
   spell[SPL_DEATH_CLOUD].chance2get_var[5] = 13;
   spell[SPL_DEATH_CLOUD].chance2get_var[6] = 12;
   spell[SPL_DEATH_CLOUD].chance2get_var[7] = 13;
   spell[SPL_DEATH_CLOUD].chance2get_var[8] = 13;
   spell[SPL_DEATH_CLOUD].ai_value[0] = 50;
   spell[SPL_DEATH_CLOUD].ai_value[1] = 50;
   spell[SPL_DEATH_CLOUD].ai_value[2] = 50;
   spell[SPL_DEATH_CLOUD].ai_value[3] = 50;
   spell[SPL_DEATH_CLOUD].description[0] =
      "{Death Cloud}\n\nDeath Cloud Description.\n";
   spell[SPL_DEATH_CLOUD].description[1] =
      "{Basic Death Cloud}\n\nBasic Death Cloud Description.\n";
   spell[SPL_DEATH_CLOUD].description[2] =
      "{Advanced Death Cloud}\n\nAdvanced Death Cloud Description.\n";
   spell[SPL_DEATH_CLOUD].description[3] =
      "{Expert Death Cloud}\n\nExpert Death Cloud Description.\n";

   // Death Blow
   spell[SPL_DEATH_BLOW].type = -1;
   spell[SPL_DEATH_BLOW].wav_name = "Deathblo.wav";
   spell[SPL_DEATH_BLOW].animation_ix = ANIM_DEATH_BLOW;
   spell[SPL_DEATH_BLOW].flags = 0x1015;
   spell[SPL_DEATH_BLOW].name = "Death Blow";
   spell[SPL_DEATH_BLOW].short_name = "Death Blow";
   spell[SPL_DEATH_BLOW].level = 5;
   spell[SPL_DEATH_BLOW].school_flags = 2;
   spell[SPL_DEATH_BLOW].mana_cost[0] = 25;
   spell[SPL_DEATH_BLOW].mana_cost[1] = 20;
   spell[SPL_DEATH_BLOW].mana_cost[2] = 20;
   spell[SPL_DEATH_BLOW].mana_cost[3] = 20;
   spell[SPL_DEATH_BLOW].eff_power = 0;
   spell[SPL_DEATH_BLOW].effect[0] = 0;
   spell[SPL_DEATH_BLOW].effect[1] = 0;
   spell[SPL_DEATH_BLOW].effect[2] = 0;
   spell[SPL_DEATH_BLOW].effect[3] = 0;
   spell[SPL_DEATH_BLOW].chance2get_var[0] = 10;
   spell[SPL_DEATH_BLOW].chance2get_var[1] = 10;
   spell[SPL_DEATH_BLOW].chance2get_var[2] = 10;
   spell[SPL_DEATH_BLOW].chance2get_var[3] = 10;
   spell[SPL_DEATH_BLOW].chance2get_var[4] = 10;
   spell[SPL_DEATH_BLOW].chance2get_var[5] = 10;
   spell[SPL_DEATH_BLOW].chance2get_var[6] = 10;
   spell[SPL_DEATH_BLOW].chance2get_var[7] = 10;
   spell[SPL_DEATH_BLOW].chance2get_var[8] = 10;
   spell[SPL_DEATH_BLOW].ai_value[0] = 50;
   spell[SPL_DEATH_BLOW].ai_value[1] = 50;
   spell[SPL_DEATH_BLOW].ai_value[2] = 50;
   spell[SPL_DEATH_BLOW].ai_value[3] = 50;
   spell[SPL_DEATH_BLOW].description[0] =
      "{Death Blow}\n\nDeath Blow Description.\n";
   spell[SPL_DEATH_BLOW].description[1] =
      "{Basic Death Blow}\n\nBasic Death Blow Description.\n";
   spell[SPL_DEATH_BLOW].description[2] =
      "{Advanced Death Blow}\n\nAdvanced Death Blow Description.\n";
   spell[SPL_DEATH_BLOW].description[3] =
      "{Expert Death Blow}\n\nExpert Death Blow Description.\n";

   for (int i = SPL_DEATH_BLOW + 1; i < SPL_DEATH_BLOW + SPECABIL_NUM; ++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];

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

   // Poison
   anim[ANIM_POISON].defName = "sp11_.def";
   anim[ANIM_POISON].name = "Poison";
   anim[ANIM_POISON].type = 1;

   // Disease
   anim[ANIM_DISEASE].defName = "sp05_.def";
   anim[ANIM_DISEASE].name = "Disease";
   anim[ANIM_DISEASE].type = 1;

   // Aging
   anim[ANIM_AGING].defName = "sp01_.def";
   anim[ANIM_AGING].name = "Aging";
   anim[ANIM_AGING].type = 1;

   // Death Cloud
   anim[ANIM_DEATH_CLOUD].defName = "sp04_.def";
   anim[ANIM_DEATH_CLOUD].name = "DeathCloud";
   anim[ANIM_DEATH_CLOUD].type = 0x10F;

   // Death Blow
   anim[ANIM_DEATH_BLOW].defName = "sp03_.def";
   anim[ANIM_DEATH_BLOW].name = "DeathBlow";
   anim[ANIM_DEATH_BLOW].type = 1;

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

Common.zip
(368.56 КБ) Скачиваний: 136

Death Cloud пока работает, как Fireball. Можно попробовать заменить 5 на 4 и 11 на 17 в таблицах, но не уверен, что этого будет достаточно, чтобы сделать его площадным.

* * *
Поправил Death Cloud (теперь площадное), анимация работает (но анимация Fireball её замещает), оставил пока функцию Fireball (для Death Cloud придётся писать свой кейс).

* * *
Потихоньку пилю функцию для Death Cloud на основе функции для Frost Ring/Fireball/Inferno/Meteor Shower/Berserk. При желании можно будет добавить ещё ударных площадных (это относительно быстро).

Картинки Disease, Aging и Death Blow получились очень чёткими, не выбиваются из общего стиля. А вот остальные, такое чувство, что мутноватые (заметно). Aging и Death Blow отнёс к Магии Огня, остальные ушли в Землю (по расцветке). В Земле и так много заклинаний, но ничего не поделаешь: зелёные - значит, Земля :smile1:

Сам не ожидал, что добавлять новые заклы окажется так легко. Конечно, какие-то с оригинальной механикой будет сложнее добавить. Но те, которые напоминают оригинальные, добавить нет проблем (хотя я против, я как раз за необычные заклинания, чтобы интересно было). Но я бы добавил призыв Феникса, например (в KB есть такое заклинание). Призыв вообще элементарно делается, но нужны картинки. Ещё прикольное было бы Кольцо кристаллов (окружает отряд кристаллами с 1 ХП), но тут реально нужна хорошая анимация (особенно разрушения), чтобы красиво выглядело.

Интересный факт. Имя анимации Aging - egA, т.е. Age наоборот.
Вернуться к началу

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

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

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

Доделал Death Cloud (формулу взял Rolex'а):

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

#define SPELLS_MAX 128
#define ANIMS_MAX 128
#define ARTIFACTS_NUM 144

#define SPELLS_NUM 76
#define ADVSPELLS_NUM 10
#define SPECABIL_NUM 11
#define SPL_FEAR 70
#define SPL_POISON 71
#define SPL_DISEASE 72
#define SPL_AGING 73
#define SPL_DEATH_CLOUD 74
#define SPL_DEATH_BLOW 75

#define ANIMS_NUM 89
#define ANIM_FEAR 83
#define ANIM_POISON 84
#define ANIM_DISEASE 85
#define ANIM_AGING 86
#define ANIM_DEATH_CLOUD 87
#define ANIM_DEATH_BLOW 88

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, 4, 4, 4, 5, 4
};

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, 17, 17, 17, 11, 17,
   // Special Abilities
   32, 33, 34, 17, 17, 34, 37, 37, 35, 37, 36
};

bool shrineSpells[SPELLS_MAX];

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

   // Fear
   spell[SPL_FEAR].type = -1;
   spell[SPL_FEAR].wav_name = "FearRoE.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";

   // Poison
   spell[SPL_POISON].type = -1;
   spell[SPL_POISON].wav_name = "Poison.wav";
   spell[SPL_POISON].animation_ix = ANIM_POISON;
   spell[SPL_POISON].flags = 0x1015;
   spell[SPL_POISON].name = "Poison";
   spell[SPL_POISON].short_name = "Poison";
   spell[SPL_POISON].level = 4;
   spell[SPL_POISON].school_flags = 8;
   spell[SPL_POISON].mana_cost[0] = 20;
   spell[SPL_POISON].mana_cost[1] = 16;
   spell[SPL_POISON].mana_cost[2] = 16;
   spell[SPL_POISON].mana_cost[3] = 16;
   spell[SPL_POISON].eff_power = 0;
   spell[SPL_POISON].effect[0] = 0;
   spell[SPL_POISON].effect[1] = 0;
   spell[SPL_POISON].effect[2] = 0;
   spell[SPL_POISON].effect[3] = 0;
   spell[SPL_POISON].chance2get_var[0] = 12;
   spell[SPL_POISON].chance2get_var[1] = 13;
   spell[SPL_POISON].chance2get_var[2] = 12;
   spell[SPL_POISON].chance2get_var[3] = 13;
   spell[SPL_POISON].chance2get_var[4] = 12;
   spell[SPL_POISON].chance2get_var[5] = 13;
   spell[SPL_POISON].chance2get_var[6] = 12;
   spell[SPL_POISON].chance2get_var[7] = 13;
   spell[SPL_POISON].chance2get_var[8] = 13;
   spell[SPL_POISON].ai_value[0] = 50;
   spell[SPL_POISON].ai_value[1] = 50;
   spell[SPL_POISON].ai_value[2] = 50;
   spell[SPL_POISON].ai_value[3] = 50;
   spell[SPL_POISON].description[0] =
      "{Poison}\n\nPoison Description.\n";
   spell[SPL_POISON].description[1] =
      "{Basic Poison}\n\nBasic Poison Description.\n";
   spell[SPL_POISON].description[2] =
      "{Advanced Poison}\n\nAdvanced Poison Description.\n";
   spell[SPL_POISON].description[3] =
      "{Expert Poison}\n\nExpert Poison Description.\n";

   // Disease
   spell[SPL_DISEASE].type = -1;
   spell[SPL_DISEASE].wav_name = "Disease.wav";
   spell[SPL_DISEASE].animation_ix = ANIM_DISEASE;
   spell[SPL_DISEASE].flags = 0x1015;
   spell[SPL_DISEASE].name = "Disease";
   spell[SPL_DISEASE].short_name = "Disease";
   spell[SPL_DISEASE].level = 2;
   spell[SPL_DISEASE].school_flags = 8;
   spell[SPL_DISEASE].mana_cost[0] = 16;
   spell[SPL_DISEASE].mana_cost[1] = 12;
   spell[SPL_DISEASE].mana_cost[2] = 12;
   spell[SPL_DISEASE].mana_cost[3] = 12;
   spell[SPL_DISEASE].eff_power = 0;
   spell[SPL_DISEASE].effect[0] = 0;
   spell[SPL_DISEASE].effect[1] = 0;
   spell[SPL_DISEASE].effect[2] = 0;
   spell[SPL_DISEASE].effect[3] = 0;
   spell[SPL_DISEASE].chance2get_var[0] = 12;
   spell[SPL_DISEASE].chance2get_var[1] = 13;
   spell[SPL_DISEASE].chance2get_var[2] = 12;
   spell[SPL_DISEASE].chance2get_var[3] = 13;
   spell[SPL_DISEASE].chance2get_var[4] = 12;
   spell[SPL_DISEASE].chance2get_var[5] = 13;
   spell[SPL_DISEASE].chance2get_var[6] = 12;
   spell[SPL_DISEASE].chance2get_var[7] = 13;
   spell[SPL_DISEASE].chance2get_var[8] = 13;
   spell[SPL_DISEASE].ai_value[0] = 50;
   spell[SPL_DISEASE].ai_value[1] = 50;
   spell[SPL_DISEASE].ai_value[2] = 50;
   spell[SPL_DISEASE].ai_value[3] = 50;
   spell[SPL_DISEASE].description[0] =
      "{Disease}\n\nDisease Description.\n";
   spell[SPL_DISEASE].description[1] =
      "{Basic Disease}\n\nBasic Disease Description.\n";
   spell[SPL_DISEASE].description[2] =
      "{Advanced Disease}\n\nAdvanced Disease Description.\n";
   spell[SPL_DISEASE].description[3] =
      "{Expert Disease}\n\nExpert Disease Description.\n";

   // Aging
   spell[SPL_AGING].type = -1;
   spell[SPL_AGING].wav_name = "Age.wav";
   spell[SPL_AGING].animation_ix = ANIM_AGING;
   spell[SPL_AGING].flags = 0x1015;
   spell[SPL_AGING].name = "Aging";
   spell[SPL_AGING].short_name = "Aging";
   spell[SPL_AGING].level = 5;
   spell[SPL_AGING].school_flags = 2;
   spell[SPL_AGING].mana_cost[0] = 25;
   spell[SPL_AGING].mana_cost[1] = 20;
   spell[SPL_AGING].mana_cost[2] = 20;
   spell[SPL_AGING].mana_cost[3] = 20;
   spell[SPL_AGING].eff_power = 0;
   spell[SPL_AGING].effect[0] = 0;
   spell[SPL_AGING].effect[1] = 0;
   spell[SPL_AGING].effect[2] = 0;
   spell[SPL_AGING].effect[3] = 0;
   spell[SPL_AGING].chance2get_var[0] = 12;
   spell[SPL_AGING].chance2get_var[1] = 13;
   spell[SPL_AGING].chance2get_var[2] = 12;
   spell[SPL_AGING].chance2get_var[3] = 13;
   spell[SPL_AGING].chance2get_var[4] = 12;
   spell[SPL_AGING].chance2get_var[5] = 13;
   spell[SPL_AGING].chance2get_var[6] = 12;
   spell[SPL_AGING].chance2get_var[7] = 13;
   spell[SPL_AGING].chance2get_var[8] = 13;
   spell[SPL_AGING].ai_value[0] = 50;
   spell[SPL_AGING].ai_value[1] = 50;
   spell[SPL_AGING].ai_value[2] = 50;
   spell[SPL_AGING].ai_value[3] = 50;
   spell[SPL_AGING].description[0] =
      "{Aging}\n\nAging Description.\n";
   spell[SPL_AGING].description[1] =
      "{Basic Aging}\n\nBasic Aging Description.\n";
   spell[SPL_AGING].description[2] =
      "{Advanced Aging}\n\nAdvanced Aging Description.\n";
   spell[SPL_AGING].description[3] =
      "{Expert Aging}\n\nExpert Aging Description.\n";

   // Death Cloud
   spell[SPL_DEATH_CLOUD].type = 0;
   spell[SPL_DEATH_CLOUD].wav_name = "Deathcld.wav";
   spell[SPL_DEATH_CLOUD].animation_ix = ANIM_DEATH_CLOUD;
   spell[SPL_DEATH_CLOUD].flags = 0x8281;
   spell[SPL_DEATH_CLOUD].name = "Death Cloud";
   spell[SPL_DEATH_CLOUD].short_name = "Death Cloud";
   spell[SPL_DEATH_CLOUD].level = 5;
   spell[SPL_DEATH_CLOUD].school_flags = 8;
   spell[SPL_DEATH_CLOUD].mana_cost[0] = 25;
   spell[SPL_DEATH_CLOUD].mana_cost[1] = 20;
   spell[SPL_DEATH_CLOUD].mana_cost[2] = 20;
   spell[SPL_DEATH_CLOUD].mana_cost[3] = 20;
   spell[SPL_DEATH_CLOUD].eff_power = 40;
   spell[SPL_DEATH_CLOUD].effect[0] = 30;
   spell[SPL_DEATH_CLOUD].effect[1] = 30;
   spell[SPL_DEATH_CLOUD].effect[2] = 60;
   spell[SPL_DEATH_CLOUD].effect[3] = 120;
   spell[SPL_DEATH_CLOUD].chance2get_var[0] = 12;
   spell[SPL_DEATH_CLOUD].chance2get_var[1] = 13;
   spell[SPL_DEATH_CLOUD].chance2get_var[2] = 12;
   spell[SPL_DEATH_CLOUD].chance2get_var[3] = 13;
   spell[SPL_DEATH_CLOUD].chance2get_var[4] = 12;
   spell[SPL_DEATH_CLOUD].chance2get_var[5] = 13;
   spell[SPL_DEATH_CLOUD].chance2get_var[6] = 12;
   spell[SPL_DEATH_CLOUD].chance2get_var[7] = 13;
   spell[SPL_DEATH_CLOUD].chance2get_var[8] = 13;
   spell[SPL_DEATH_CLOUD].ai_value[0] = 50;
   spell[SPL_DEATH_CLOUD].ai_value[1] = 50;
   spell[SPL_DEATH_CLOUD].ai_value[2] = 50;
   spell[SPL_DEATH_CLOUD].ai_value[3] = 50;
   spell[SPL_DEATH_CLOUD].description[0] =
      "{Death Cloud}\n\nDeath Cloud Description.\n";
   spell[SPL_DEATH_CLOUD].description[1] =
      "{Basic Death Cloud}\n\nBasic Death Cloud Description.\n";
   spell[SPL_DEATH_CLOUD].description[2] =
      "{Advanced Death Cloud}\n\nAdvanced Death Cloud Description.\n";
   spell[SPL_DEATH_CLOUD].description[3] =
      "{Expert Death Cloud}\n\nExpert Death Cloud Description.\n";

   // Death Blow
   spell[SPL_DEATH_BLOW].type = -1;
   spell[SPL_DEATH_BLOW].wav_name = "Deathblo.wav";
   spell[SPL_DEATH_BLOW].animation_ix = ANIM_DEATH_BLOW;
   spell[SPL_DEATH_BLOW].flags = 0x1015;
   spell[SPL_DEATH_BLOW].name = "Death Blow";
   spell[SPL_DEATH_BLOW].short_name = "Death Blow";
   spell[SPL_DEATH_BLOW].level = 5;
   spell[SPL_DEATH_BLOW].school_flags = 2;
   spell[SPL_DEATH_BLOW].mana_cost[0] = 25;
   spell[SPL_DEATH_BLOW].mana_cost[1] = 20;
   spell[SPL_DEATH_BLOW].mana_cost[2] = 20;
   spell[SPL_DEATH_BLOW].mana_cost[3] = 20;
   spell[SPL_DEATH_BLOW].eff_power = 0;
   spell[SPL_DEATH_BLOW].effect[0] = 0;
   spell[SPL_DEATH_BLOW].effect[1] = 0;
   spell[SPL_DEATH_BLOW].effect[2] = 0;
   spell[SPL_DEATH_BLOW].effect[3] = 0;
   spell[SPL_DEATH_BLOW].chance2get_var[0] = 10;
   spell[SPL_DEATH_BLOW].chance2get_var[1] = 10;
   spell[SPL_DEATH_BLOW].chance2get_var[2] = 10;
   spell[SPL_DEATH_BLOW].chance2get_var[3] = 10;
   spell[SPL_DEATH_BLOW].chance2get_var[4] = 10;
   spell[SPL_DEATH_BLOW].chance2get_var[5] = 10;
   spell[SPL_DEATH_BLOW].chance2get_var[6] = 10;
   spell[SPL_DEATH_BLOW].chance2get_var[7] = 10;
   spell[SPL_DEATH_BLOW].chance2get_var[8] = 10;
   spell[SPL_DEATH_BLOW].ai_value[0] = 50;
   spell[SPL_DEATH_BLOW].ai_value[1] = 50;
   spell[SPL_DEATH_BLOW].ai_value[2] = 50;
   spell[SPL_DEATH_BLOW].ai_value[3] = 50;
   spell[SPL_DEATH_BLOW].description[0] =
      "{Death Blow}\n\nDeath Blow Description.\n";
   spell[SPL_DEATH_BLOW].description[1] =
      "{Basic Death Blow}\n\nBasic Death Blow Description.\n";
   spell[SPL_DEATH_BLOW].description[2] =
      "{Advanced Death Blow}\n\nAdvanced Death Blow Description.\n";
   spell[SPL_DEATH_BLOW].description[3] =
      "{Expert Death Blow}\n\nExpert Death Blow Description.\n";

   for (int i = SPL_DEATH_BLOW + 1; i < SPL_DEATH_BLOW + SPECABIL_NUM; ++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];

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

   // Poison
   anim[ANIM_POISON].defName = "sp11_.def";
   anim[ANIM_POISON].name = "Poison";
   anim[ANIM_POISON].type = 1;

   // Disease
   anim[ANIM_DISEASE].defName = "sp05_.def";
   anim[ANIM_DISEASE].name = "Disease";
   anim[ANIM_DISEASE].type = 1;

   // Aging
   anim[ANIM_AGING].defName = "sp01_.def";
   anim[ANIM_AGING].name = "Aging";
   anim[ANIM_AGING].type = 1;

   // Death Cloud
   anim[ANIM_DEATH_CLOUD].defName = "sp04_.def";
   anim[ANIM_DEATH_CLOUD].name = "DeathCloud";
   anim[ANIM_DEATH_CLOUD].type = 1;

   // Death Blow
   anim[ANIM_DEATH_BLOW].defName = "sp03_.def";
   anim[ANIM_DEATH_BLOW].name = "DeathBlow";
   anim[ANIM_DEATH_BLOW].type = 1;

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

int __stdcall ShrineSpellsInit(LoHook* h, HookContext* c)
{
    memcpy(&shrineSpells, &o_GameMgr->disabled_shrines, sizeof(shrineSpells) / sizeof(bool));   

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

int __fastcall fillShrines(_GameMgr_* game, int unused_edx, int shrineFlags)
{
    int availSpellsNum = 0;
   
    for (int i = 0; i < SPELLS_NUM; ++i)
    {
        int lvl = o_Spell[i].level - 1;
        if (lvl < 5 && 1 << lvl & shrineFlags && o_Spell[i].school_flags && !shrineSpells[i])
            ++availSpellsNum;
    }
   
    int spell = 0;
    int chosenSpell = 0;
   
    if (availSpellsNum)
    {
        int rndSpell = Randint(0, availSpellsNum - 1);
       
        for (int i = 0; i < SPELLS_NUM; ++i)
        {
            int lvl = o_Spell[i].level - 1;
            if (lvl < 5 && 1 << lvl & shrineFlags && o_Spell[i].school_flags && !shrineSpells[i])
            {
                if (spell == rndSpell) break;
                ++spell;
            }
            ++chosenSpell;
        }
       
        shrineSpells[chosenSpell] = true;
    }
    else
    {
        spell = 0;
       
        for (int i = 0; i < SPELLS_NUM; ++i)
        {
            int lvl = o_Spell[i].level - 1;
            if (lvl < 5 && 1 << lvl & shrineFlags && o_Spell[i].school_flags && !game->disabled_shrines[i])
            {
                shrineSpells[i] = false;
                ++spell;
            }
        }
       
        if (!spell)
            chosenSpell = ID_NONE;
        else
            chosenSpell = fillShrines(game, unused_edx, shrineFlags);
    }

    return chosenSpell;
}

int __stdcall DeathCloud(LoHook* h, HookContext* c)
{
    if (c->edx == SPL_DEATH_CLOUD)
    {
        CALL_5(void, __thiscall, 0x5A4C80, o_BattleMgr, *(int*)(c->ebp + 0xC), SPL_DEATH_CLOUD, c->esi, *(int*)(c->ebp + 0x1C));
       
        c->return_address = 0x5A2368;
        return NO_EXEC_DEFAULT;
    }
   
    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_));
                           
         // 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);

         // ----------------------------------------
         // New _GameMgr_.disabled_spells[140] field
         // ----------------------------------------
         _PI->WriteByte(0x4C16ED, 4); // Pyramids
         _PI->WriteByte(0x4C254C, 4); // Disable Creature Spells Routine
         _PI->WriteByte(0x4C25F1, 4); // Disable Creature Spells Routine
         _PI->WriteByte(0x501297, 4); // Scholars
         _PI->WriteByte(0x5BEA55, 4); // Mage Guild

         // Clear _GameMgr_.disabled_spells[140] (optional)
         _PI->WriteDword(0x4BEFB8, 0x23);
         _PI->WriteJmp  (0x4BEFBE, 0x4BEFCE);
         
         // 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 - ADVSPELLS_NUM);
         _PI->WriteDword(0x59EFE4, (int)&spellIndirectTableA);
         // _PI->WriteByte  (0x5A064E, ANIMS_NUM - 1 - ADVSPELLS_NUM);
         _PI->WriteDword(0x5A0659, (int)&spellIndirectTableB);
         _PI->WriteByte (0x4963E9, ANIMS_NUM);
         _PI->WriteByte (0x4965BD, ANIMS_NUM);

         // Shrine Spells
         _PI->WriteLoHook(0x4C2625, ShrineSpellsInit);
         _PI->WriteHiHook(0x4C9260, SPLICE_, DIRECT_, THISCALL_, fillShrines);
         
         // Death Cloud
         _PI->WriteLoHook(0x5A0F4C, DeathCloud);

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

   return TRUE;
}

UPD: Разобрался с полем disabled_spells[SPELLS_NUM]. Теперь всё должно быть OK. Пришлось переписать функцию генерации заклинаний в святилищах.

При тестах я часто рестарчу карту, так вот обнаружил плавающие вылеты 0x4078D5 и 0x4079A5, если несколько раз рестартить. Сначала подумал, что ошибка плагина. Отключил все плагины, убрал ресурсы из Common. Вылеты остаются (с HD+ и без). Может 30 рестартов не вылетать, а может на 3-4 вылететь.

Следующий этап - сдвиг абилок в позицию 110, чтобы работали :smile1: 0-109 - заклинания будут, а 110-127 - абилки существ.

* * *
Начал переносить спецабилки и наткнулся на ещё один подводный камень:

Код: Выделить всё
_int32_ active_spell_duration[81]; // +408 0x198  есть заклинание (длительность) или нет
_int32_ active_spells_power[81]; // +732 0x2DC (сила действия заклинания)

Пока у нас было меньше 81 заклинания, он не был заметен. Теперь абилки начинаются с индекса 110, а посему эти массивы оказываются тесными.

Хорошо, что под элемент массива отводится 4 байта, когда 2-х более чем достаточно. 65535 раундов? Серьёзно? :smile1: Сначала подумал заменить инструкции. Hапример, mov ecx, [esi+ebx*4+198h] на movzx ecx, word [esi+ebx*2+198h], но размер первой 7 байт, а второй - 8, да и не универсально это. Придётся хукать :smile1: Благо, я посчитал, что обращений к этим полям не так много.
Вернуться к началу

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

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

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

Очень круто переписал начало функции applySpell() (sub_444610), но как посмотрел, сколько ещё всего нужно править для того, чтобы игра работала с массивами active_spell_duration и active_spells_power, как с имеющими 2-х байтовые элементы, руки опустились :smile15: Нет, это подъёмная работа - 16 хуков, включая уже закрытые в applySpell(), и не забыть про код заклинаний защиты от стихий, - но, значит, стал я смотреть, как игра работает с active_spells_power (+2DCh) (на самом деле, это поле хранит уровень магической школы наложенного заклинания; название сбивает с толку). Так вот, подозрительно мало она работает с этим полем, если я, конечно, всё увидел: это поле апдейтится в applySpell(), но я не вижу пока кода, который использует (!) значения, записанные туда. Игра хранит бонусы/штрафы к характеристикам, и когда время действия заклинания прекращается либо его снимают, она вычитает бонусы и прибавляет штрафы, т.е. не вычисляет их на основе уровня школы магии наложенного заклинания (не использует поле +2DCh).

:?: Такой вопрос: зачем тогда игре хранить уровень школы магии наложенного заклинания вместе с его длительностью? Есть предположения? И нельзя ли использовать active_spells_power как продолжение active_spell_duration, что избавило бы от дополнительных хуков и, самое главное, потенциальных ошибок?

* * *
Пришёл к выводу, что перенос спецабилок - плохая идея. Есть код, который проходит по всем заклам И спецабилкам, а поэтому нужна непрерывность. Т.е. новые заклинания теперь будут сразу после спецабилок. Как же запретить спецабилкам генериться в качестве заклинаний тогда? Это было бы проблемой, если бы не расширенный массив disabled_spells[140], который реально спасает. Достаточно отключить спецабилки :smile2:

Если я прав насчёт массива active_spells_power, то останется добавить несколько пропущенных патчей (исправить 81 = кол-во оригинальных заклинаний + кол-во спецабилок на общее кол-во заклинаний и спецабилок) и плагин в техническом плане готов (и даже с работающим Death Cloud) :smile18: Написание кода для других заклинаний, как Вы понимаете, творческий процесс и потребует больше времени, чем ушло на "фундамент". Можно, конечно, налепить с десяток аналогов Frost Ring, Fireball и Inferno (как Вам, например, заклинание Blizzard: Inferno, но только синее? шучу), но я сейчас говорю о заклинаниях, которые будут действительно интересны. Как минимум, те заклы, которые уже добавлены, постараюсь закончить после KB II (а может, в перерывах от).

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

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

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

Сообщение Rolex » 23 авг 2021, 16:32

AlexSpl писал(а):

Картинки Disease, Aging и Death Blow получились очень чёткими, не выбиваются из общего стиля. А вот остальные, такое чувство, что мутноватые (заметно).

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

Вы можете заметить, что первое заклинание во втором ряду, не зависимо от того наше оно или родное, всегда находится на мутном фоне. Например, Удар Молнии или Уничтожении нежити. Просто такое не со всеми заклами, плюс большинство родных занимают большую площадь по ширине и это почти незаметно. Да и вы ранее в качество родных заклов сильно не всматривались, не придавали этому значения, а потому просто не замечали подобного.

Вот как раз таки наши Death Cloud и Poison и попали на мутный фон. Но если для проверки мутности сделать их заклами 4-го уровня, то они перестанут быть такими, фон изменится в лучшую сторону. Кстати, а вот Death Blow немного не поместился и в результате появились голубые пиксели на концах, которые не поместились. Но я сейчас ее исправил, включил в архив, теперь должно быть ок. Замените у себя.

AlexSpl писал(а):

Но я бы добавил призыв Феникса, например (в KB есть такое заклинание). Призыв вообще элементарно делается, но нужны картинки.

Наверное, тогда уже Призыв Фениксов. Ибо всега призывать 1 Феникса с любой СМ плохо. Но нужно держать баланс: суммарная сила призваных Фениксов не должна быть существенно сильнее суммарной силы призваных Элементалей Земли на одном уровне развития школы магии. Нужно выходить плюс-минус на тот же уровень, хотя небольшие отклонения в ту или иную сторону, там до 10% или даже до 20% вполне нормально.

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

Варианты:
Элементали психики + Огненные птицы
Элементали психики + Фениксы
Элементали магии + Фениксы

На ваше усмотрение. По призыву предлагаю следующие формулы:

Элементали психики / Элементали магии:
Базовый: СМ
Продвинутый: СМ * 1,5
Экспертный: СМ * 2

Огненные птицы:
Базовый: СМ * 0,5
Продвинутый: СМ * 0,75
Экспертный: СМ

Фениксы:
Базовый: СМ * 0,25
Продвинутый: СМ * 0,375
Экспертный: СМ * 0,5

Изображение Изображение

Изображение Изображение

Изображение Изображение

AlexSpl писал(а):

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

Ну у нас же не KB и даже не WoG. Кристаллы с ХП вокруг отряда во время боя? Какая-то дичь как по мне. Я так понял, что идея как с единичками, прикрыть основной отряд, чтобы враг потратил свою атаку, чтобы добраться к основному отряду.

Нам нужно что-то, чтобы прикрыть основной отряд и при этом это что-то должно погибать абсолютно от любой атаки и урона. Для этого отлично подойдет Кольцо крестьян. У них всего 1 хп, а урон не может быть менее 1. Другое дело как этот закл разбить на уровни.

Типа на Базовом Крестьянин погибает без возрождения, на Продвинутом возрождается 1 раз, на Экспертном 2 раза после гибели. Но это бредятина. Можно поступить иначе, окружать отряд каким-то 1 гексвым препятствием, которые бывают на поле боя, и делать возможным атаку существами этого препятствия. Типа 1/2/3 атаки на Базовом/Продвинутом/Экспертном и препятствие разрушается в момент разрушения можно проигрывать анимацию разрушения препятствий. Но вопрос в том, сможете ли вы подобное реализовать. Если сможете, то картинки я сделаю.

AlexSpl писал(а):

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

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

Кстати, Disease (Болезнь) нужно сделать 4-го уровня. Мы его немного прокачаем, я хочу его сделать АнтиМолитвой. Хочу добавить к нему эффект Медлительности 20%/40%/40% для всех отрядов. На Эксперте он массовый, как и Молитва.

Плюс Death Blow у Вас сейчас накладывается на вражеский отряд, а должен как Бешенство на свой отряд. Анимацию при наложенииний на свой отряд и перед атакой на вражеский используем одну и ту же. Длительность действия, как и с Бешенством в течении 1 раунда. 1 атака и одна 1 ответка с повышенным уроном.

AlexSpl писал(а):

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

Ничего страшного. Пускай будет. При наведении курсора на картинку мы увидим длительность его действия и в случае с абилками там будет 3 раунда, а с заклом кол-во раундов будет равно СМ.

Другой вариант: при наложении абилки на отряд под заклом с этой абилкой, проверять остаток раундов этого закла. Если оно меньше 3, то не добавляем картинку, а просто устанавливаем большее кол-во раундов в данном случае 3. Если же остаток раундов длительности этого закла 3 и более, то не добавляем картинку с длительностью абилки.

Аналогично и с Aging и Poison. Если отряд под абилкой, то при наложении этого же закла с длительностью больше остатка абилки, просто обновляем кол-во раундов на большее, которое соотвествует силе магии героя, но при этом картинку не дублируем, а оставляем одну.

Кстати, как в WoG, так и в HotA нет ни одного нового заклинания. В ERA, кстати, нельзя добалять новые заклы, а только менять хар-ки уже существующих.

7 новых заклов были добавлены только в MoP. Но редактор заклинаний MoP также не позволяет добавлять новые заклы. Только редактировать уже существующие.

Вот здесь было обсуждение:
http://wforum.heroes35.net/showthread.p ... 241&page=2

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

Кстати, MoP писался полностью на асме. Там 3 походных закла и 4 боевых. Но часть из них малопелезны, а другая часть по большей части имбовые и больше подходяд WoG. По сути мод MoP и базируется на аддоне WoG. Наши же заклы очень хорошо вписуются в SoD не нарушая баланс и отлично бы подошли HotA.

Если же делать как я предлагал с возможностью откл заклов через ini-файл. Fear = 1 - закл генерируется в гильдии и попадает в книгу, Fear = 0 - закл откл, как будто его нет. То, думаю, можно взять и добавить 1-2 закла из MoP. Например, интересные Сбор войск и Притяжение.

Сбор войск интересно реализовано:
Базовый: открывается ближайшее внешнее жилище к герою в пределах экрана.
Продвинутый: дает возможность выбрать любое внешнее жилище, которое находится в пределах экрана с героем.
Эксперт: позволяет выбрать абсолютно любое подконтрольное жилище на карте.

При наведении на жилище иконка курсора меняется и становится такой как при наведении на город.

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

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

 Описание заклов MoP:
Походные
Факел — простое и дешевое заклинание, позволяющее увеличить на день радиус обзора героя в подземелье. Экспертное колдовство Факела полностью компенсирует недостаток обзора тем, кто плохо видит под землей — а это все герои, кроме героев «подземных» городов — Инферно и Темницы.

Сбор Войск — одно из полезнейших заклинаний, способное избавить героя от еженедельной беготни от одного жилища существ к другому. Теперь, с помощью Сбора Войск, это можно делать удаленно. С повышением уровня колдовства растут и возможности заклинания, а экспертный Сбор Войск позволяет герою покупать существ в любом внешнем жилище
под флагом родного королевства. Стоит лишь навести курсор на жилище и щелкнуть мышью... Отмена колдовства заклинания осуществляется так же, как и с Дверью Измерений — ПКМ на карте.

Встреча Героев — заклинание, важность которого трудно переоценить. Если Городской Портал требует перемещения героя, а Подкрепления позволяют взять из города только армию гарнизона, то Встреча Героев позволяет обмениваться всем, чем только могут обмениваться герои. При этом даже действует Грамотность. Фактически, заклинание уничтожает
сложившуюся в игре тактику «цепочек» — способ доставки армий с помощью героев, находящихся в дне ходьбы друг от друга. Герой, обладающий этим заклинанием, может идти победоносным маршем по карте, не испытывая нужды в возвращении назад за подкреплениями или артефактами.
Однако, за подобную возможность приходится платить весьма немалым количеством маны, что в новых условиях ее восполнения значительно снижает эффективность использования заклинания.

Боевые
Притяжение позволяет лишить вражеских существ способности к преодолению препятствий (см. флаг «Летает» редактора существ). Это может быть особенно полезно при осаде города. Заклинание, в зависимости от развития навыка героя, действует на существ до определенного уровня и всегда имеет массовое воздействие. Кроме природной способности существ к полету, оно также нейтрализует действие Кольца Левитации.

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

Вызов Светлячков является продолжением линейки вызовов элементалей в сторону понижения уровня. Это заклинание с 5%-ым шансом появления доступно в любом городе и принадлежит всем школам магии. Призываемый юнит соответствует уровню заклинания: Светлячок — слабое существо с единственной отличительной чертой — иммунитетом к заклинаниям первого уровня.

Важно, что Вызов Светлячков не мешает вызову после этого элементалей.

Вьюга — главное ударное заклинание Магии Воды. Наряду с Ледяной Молнией и Кольцом Холода, Вьюга входит в тройку так называемых «морозных заклинаний». Как и они, она наносит повышенный урон Огненным и Энергетическим Элементалям и, в то же время, бесполезна против Ледяных Элементалей или отрядов, защищенных Ледяным Щитом. Уникальной чертой Вьюги, отличающей ее от других ударных заклинаний, является ее массовое воздействие на экспертном уровне навыка. Это сразу делает ее мощнее в несколько раз, ибо урон, наносимый ранее лишь одному отряду,
поражает каждый вражеский стек.

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

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

Исходники MoP v3.07 (пароль=123)
https://drive.google.com/uc?export=down ... B5Tfr7g4Q1

Мануал MoP v3.07:
https://drive.google.com/file/d/1XiIMHQ ... IIoP6/view

Мой архив с картинками существ для призывы (плюс исправленный Death Blow):
Вложения
Images_FireBird_Phenix_Psychic_Magic_Elem.rar
(87.09 КБ) Скачиваний: 135
Последний раз редактировалось Rolex 23 авг 2021, 17:50, всего редактировалось 4 раз(а).
Вернуться к началу

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

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

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

Цитата:
Насколько мне известно, Вы неплохо знаете Ассемблер. В таком случае Вам может будет интересно, а может и полезно глянуть исходный код MoP, а именно код по добавлению новых заклов:

Спасибо. Это определённо пригодится (вдруг какие адреса пропустил).

Делаю def'ы под новый порядок заклинаний. Идея сдвигать абилки бредовая :smile14: Сейчас думаю, сколько кода нужно было бы перелопатить, чтобы все подвинуть. Огромный плюс оставить абилки на месте в том, что они сразу будут работать без проблем.
Вернуться к началу

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

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

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

Вот пока что получилось (версия для тестирования):

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

#define SPELLS_MAX 128
#define ANIMS_MAX 128
#define ADVSPELLS_NUM 10
#define SPECABIL_NUM 11
#define ARTIFACTS_NUM 144

#define SPELLS_NUM 87
#define SPL_FEAR_NEW 81
#define SPL_POISON_NEW 82
#define SPL_DISEASE_NEW 83
#define SPL_AGING_NEW 84
#define SPL_DEATH_CLOUD_NEW 85
#define SPL_DEATH_BLOW_NEW 86

#define ANIMS_NUM 89
#define ANIM_FEAR_NEW 83
#define ANIM_POISON_NEW 84
#define ANIM_DISEASE_NEW 85
#define ANIM_AGING_NEW 86
#define ANIM_DEATH_CLOUD_NEW 87
#define ANIM_DEATH_BLOW_NEW 88

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 (starting from Quicksand #10)
    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,
   // Special Abilities (you cannot cast them anyway)
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1,
   // New Spells (starting from Fear #81)
   4, 4, 4, 4, 5, 4
};

char spellIndirectTableB[] = {
   // Spells (starting from Quicksand #10)
    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,
   // Special Abilities
   32, 33, 34, 17, 17, 34, 37, 37, 35, 37,
   36,
   // New Spells (starting from Fear #81)
   17, 17, 17, 17, 11, 17
};

char spellIndirectTableC[] = {
   // Spells (starting from Shield #27)
    0,  1,  2,  3,  4,  5,  6,  7, 32,  8,
   32, 32, 32, 32,  9, 10, 11, 12, 13, 14,
   32, 15, 16, 17, 18, 19, 20, 21, 22, 23,
   32, 24, 25, 26, 27, 28, 32, 32, 32, 32,
   32, 32, 32,
   // Special Abilities
   32, 29, 32, 30, 32, 31, -1, -1, -1, -1,
   -1,
   // New Spells (starting from Fear #81)
   32, 32, 32, 32, 32, 32
};

bool shrineSpells[SPELLS_MAX];

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

   // Fear
   spell[SPL_FEAR_NEW].type = -1;
   spell[SPL_FEAR_NEW].wav_name = "FearRoE.wav";
   spell[SPL_FEAR_NEW].animation_ix = ANIM_FEAR_NEW;
   spell[SPL_FEAR_NEW].flags = 0x20415;
   spell[SPL_FEAR_NEW].name = "Fear";
   spell[SPL_FEAR_NEW].short_name = "Fear";
   spell[SPL_FEAR_NEW].level = 4;
   spell[SPL_FEAR_NEW].school_flags = 8;
   spell[SPL_FEAR_NEW].mana_cost[0] = 16;
   spell[SPL_FEAR_NEW].mana_cost[1] = 8;
   spell[SPL_FEAR_NEW].mana_cost[2] = 8;
   spell[SPL_FEAR_NEW].mana_cost[3] = 8;
   spell[SPL_FEAR_NEW].eff_power = 0;
   spell[SPL_FEAR_NEW].effect[0] = 75;
   spell[SPL_FEAR_NEW].effect[1] = 75;
   spell[SPL_FEAR_NEW].effect[2] = 50;
   spell[SPL_FEAR_NEW].effect[3] = 25;
   spell[SPL_FEAR_NEW].chance2get_var[0] = 10;
   spell[SPL_FEAR_NEW].chance2get_var[1] = 10;
   spell[SPL_FEAR_NEW].chance2get_var[2] = 10;
   spell[SPL_FEAR_NEW].chance2get_var[3] = 10;
   spell[SPL_FEAR_NEW].chance2get_var[4] = 10;
   spell[SPL_FEAR_NEW].chance2get_var[5] = 10;
   spell[SPL_FEAR_NEW].chance2get_var[6] = 10;
   spell[SPL_FEAR_NEW].chance2get_var[7] = 10;
   spell[SPL_FEAR_NEW].chance2get_var[8] = 10;
   spell[SPL_FEAR_NEW].ai_value[0] = 50;
   spell[SPL_FEAR_NEW].ai_value[1] = 50;
   spell[SPL_FEAR_NEW].ai_value[2] = 50;
   spell[SPL_FEAR_NEW].ai_value[3] = 50;
   spell[SPL_FEAR_NEW].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_NEW].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_NEW].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_NEW].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";

   // Poison
   spell[SPL_POISON_NEW].type = -1;
   spell[SPL_POISON_NEW].wav_name = "Poison.wav";
   spell[SPL_POISON_NEW].animation_ix = ANIM_POISON_NEW;
   spell[SPL_POISON_NEW].flags = 0x1015;
   spell[SPL_POISON_NEW].name = "Poison";
   spell[SPL_POISON_NEW].short_name = "Poison";
   spell[SPL_POISON_NEW].level = 4;
   spell[SPL_POISON_NEW].school_flags = 8;
   spell[SPL_POISON_NEW].mana_cost[0] = 20;
   spell[SPL_POISON_NEW].mana_cost[1] = 16;
   spell[SPL_POISON_NEW].mana_cost[2] = 16;
   spell[SPL_POISON_NEW].mana_cost[3] = 16;
   spell[SPL_POISON_NEW].eff_power = 0;
   spell[SPL_POISON_NEW].effect[0] = 0;
   spell[SPL_POISON_NEW].effect[1] = 0;
   spell[SPL_POISON_NEW].effect[2] = 0;
   spell[SPL_POISON_NEW].effect[3] = 0;
   spell[SPL_POISON_NEW].chance2get_var[0] = 10;
   spell[SPL_POISON_NEW].chance2get_var[1] = 10;
   spell[SPL_POISON_NEW].chance2get_var[2] = 10;
   spell[SPL_POISON_NEW].chance2get_var[3] = 10;
   spell[SPL_POISON_NEW].chance2get_var[4] = 10;
   spell[SPL_POISON_NEW].chance2get_var[5] = 10;
   spell[SPL_POISON_NEW].chance2get_var[6] = 10;
   spell[SPL_POISON_NEW].chance2get_var[7] = 10;
   spell[SPL_POISON_NEW].chance2get_var[8] = 10;
   spell[SPL_POISON_NEW].ai_value[0] = 50;
   spell[SPL_POISON_NEW].ai_value[1] = 50;
   spell[SPL_POISON_NEW].ai_value[2] = 50;
   spell[SPL_POISON_NEW].ai_value[3] = 50;
   spell[SPL_POISON_NEW].description[0] =
      "{Poison}\n\nPoison Description.\n";
   spell[SPL_POISON_NEW].description[1] =
      "{Basic Poison}\n\nBasic Poison Description.\n";
   spell[SPL_POISON_NEW].description[2] =
      "{Advanced Poison}\n\nAdvanced Poison Description.\n";
   spell[SPL_POISON_NEW].description[3] =
      "{Expert Poison}\n\nExpert Poison Description.\n";

   // Disease
   spell[SPL_DISEASE_NEW].type = -1;
   spell[SPL_DISEASE_NEW].wav_name = "Disease.wav";
   spell[SPL_DISEASE_NEW].animation_ix = ANIM_DISEASE_NEW;
   spell[SPL_DISEASE_NEW].flags = 0x1015;
   spell[SPL_DISEASE_NEW].name = "Disease";
   spell[SPL_DISEASE_NEW].short_name = "Disease";
   spell[SPL_DISEASE_NEW].level = 4;
   spell[SPL_DISEASE_NEW].school_flags = 8;
   spell[SPL_DISEASE_NEW].mana_cost[0] = 16;
   spell[SPL_DISEASE_NEW].mana_cost[1] = 12;
   spell[SPL_DISEASE_NEW].mana_cost[2] = 12;
   spell[SPL_DISEASE_NEW].mana_cost[3] = 12;
   spell[SPL_DISEASE_NEW].eff_power = 0;
   spell[SPL_DISEASE_NEW].effect[0] = 0;
   spell[SPL_DISEASE_NEW].effect[1] = 0;
   spell[SPL_DISEASE_NEW].effect[2] = 0;
   spell[SPL_DISEASE_NEW].effect[3] = 0;
   spell[SPL_DISEASE_NEW].chance2get_var[0] = 10;
   spell[SPL_DISEASE_NEW].chance2get_var[1] = 10;
   spell[SPL_DISEASE_NEW].chance2get_var[2] = 10;
   spell[SPL_DISEASE_NEW].chance2get_var[3] = 10;
   spell[SPL_DISEASE_NEW].chance2get_var[4] = 10;
   spell[SPL_DISEASE_NEW].chance2get_var[5] = 10;
   spell[SPL_DISEASE_NEW].chance2get_var[6] = 10;
   spell[SPL_DISEASE_NEW].chance2get_var[7] = 10;
   spell[SPL_DISEASE_NEW].chance2get_var[8] = 10;
   spell[SPL_DISEASE_NEW].ai_value[0] = 50;
   spell[SPL_DISEASE_NEW].ai_value[1] = 50;
   spell[SPL_DISEASE_NEW].ai_value[2] = 50;
   spell[SPL_DISEASE_NEW].ai_value[3] = 50;
   spell[SPL_DISEASE_NEW].description[0] =
      "{Disease}\n\nDisease Description.\n";
   spell[SPL_DISEASE_NEW].description[1] =
      "{Basic Disease}\n\nBasic Disease Description.\n";
   spell[SPL_DISEASE_NEW].description[2] =
      "{Advanced Disease}\n\nAdvanced Disease Description.\n";
   spell[SPL_DISEASE_NEW].description[3] =
      "{Expert Disease}\n\nExpert Disease Description.\n";

   // Aging
   spell[SPL_AGING_NEW].type = -1;
   spell[SPL_AGING_NEW].wav_name = "Age.wav";
   spell[SPL_AGING_NEW].animation_ix = ANIM_AGING_NEW;
   spell[SPL_AGING_NEW].flags = 0x1015;
   spell[SPL_AGING_NEW].name = "Aging";
   spell[SPL_AGING_NEW].short_name = "Aging";
   spell[SPL_AGING_NEW].level = 5;
   spell[SPL_AGING_NEW].school_flags = 2;
   spell[SPL_AGING_NEW].mana_cost[0] = 25;
   spell[SPL_AGING_NEW].mana_cost[1] = 20;
   spell[SPL_AGING_NEW].mana_cost[2] = 20;
   spell[SPL_AGING_NEW].mana_cost[3] = 20;
   spell[SPL_AGING_NEW].eff_power = 0;
   spell[SPL_AGING_NEW].effect[0] = 0;
   spell[SPL_AGING_NEW].effect[1] = 0;
   spell[SPL_AGING_NEW].effect[2] = 0;
   spell[SPL_AGING_NEW].effect[3] = 0;
   spell[SPL_AGING_NEW].chance2get_var[0] = 10;
   spell[SPL_AGING_NEW].chance2get_var[1] = 10;
   spell[SPL_AGING_NEW].chance2get_var[2] = 10;
   spell[SPL_AGING_NEW].chance2get_var[3] = 10;
   spell[SPL_AGING_NEW].chance2get_var[4] = 10;
   spell[SPL_AGING_NEW].chance2get_var[5] = 10;
   spell[SPL_AGING_NEW].chance2get_var[6] = 10;
   spell[SPL_AGING_NEW].chance2get_var[7] = 10;
   spell[SPL_AGING_NEW].chance2get_var[8] = 10;
   spell[SPL_AGING_NEW].ai_value[0] = 50;
   spell[SPL_AGING_NEW].ai_value[1] = 50;
   spell[SPL_AGING_NEW].ai_value[2] = 50;
   spell[SPL_AGING_NEW].ai_value[3] = 50;
   spell[SPL_AGING_NEW].description[0] =
      "{Aging}\n\nAging Description.\n";
   spell[SPL_AGING_NEW].description[1] =
      "{Basic Aging}\n\nBasic Aging Description.\n";
   spell[SPL_AGING_NEW].description[2] =
      "{Advanced Aging}\n\nAdvanced Aging Description.\n";
   spell[SPL_AGING_NEW].description[3] =
      "{Expert Aging}\n\nExpert Aging Description.\n";

   // Death Cloud
   spell[SPL_DEATH_CLOUD_NEW].type = 0;
   spell[SPL_DEATH_CLOUD_NEW].wav_name = "Deathcld.wav";
   spell[SPL_DEATH_CLOUD_NEW].animation_ix = ANIM_DEATH_CLOUD_NEW;
   spell[SPL_DEATH_CLOUD_NEW].flags = 0x8281;
   spell[SPL_DEATH_CLOUD_NEW].name = "Death Cloud";
   spell[SPL_DEATH_CLOUD_NEW].short_name = "Death Cloud";
   spell[SPL_DEATH_CLOUD_NEW].level = 5;
   spell[SPL_DEATH_CLOUD_NEW].school_flags = 8;
   spell[SPL_DEATH_CLOUD_NEW].mana_cost[0] = 25;
   spell[SPL_DEATH_CLOUD_NEW].mana_cost[1] = 20;
   spell[SPL_DEATH_CLOUD_NEW].mana_cost[2] = 20;
   spell[SPL_DEATH_CLOUD_NEW].mana_cost[3] = 20;
   spell[SPL_DEATH_CLOUD_NEW].eff_power = 40;
   spell[SPL_DEATH_CLOUD_NEW].effect[0] = 30;
   spell[SPL_DEATH_CLOUD_NEW].effect[1] = 30;
   spell[SPL_DEATH_CLOUD_NEW].effect[2] = 60;
   spell[SPL_DEATH_CLOUD_NEW].effect[3] = 120;
   spell[SPL_DEATH_CLOUD_NEW].chance2get_var[0] = 10;
   spell[SPL_DEATH_CLOUD_NEW].chance2get_var[1] = 10;
   spell[SPL_DEATH_CLOUD_NEW].chance2get_var[2] = 10;
   spell[SPL_DEATH_CLOUD_NEW].chance2get_var[3] = 10;
   spell[SPL_DEATH_CLOUD_NEW].chance2get_var[4] = 10;
   spell[SPL_DEATH_CLOUD_NEW].chance2get_var[5] = 10;
   spell[SPL_DEATH_CLOUD_NEW].chance2get_var[6] = 10;
   spell[SPL_DEATH_CLOUD_NEW].chance2get_var[7] = 10;
   spell[SPL_DEATH_CLOUD_NEW].chance2get_var[8] = 10;
   spell[SPL_DEATH_CLOUD_NEW].ai_value[0] = 50;
   spell[SPL_DEATH_CLOUD_NEW].ai_value[1] = 50;
   spell[SPL_DEATH_CLOUD_NEW].ai_value[2] = 50;
   spell[SPL_DEATH_CLOUD_NEW].ai_value[3] = 50;
   spell[SPL_DEATH_CLOUD_NEW].description[0] =
      "{Death Cloud}\n\nDeath Cloud Description.\n";
   spell[SPL_DEATH_CLOUD_NEW].description[1] =
      "{Basic Death Cloud}\n\nBasic Death Cloud Description.\n";
   spell[SPL_DEATH_CLOUD_NEW].description[2] =
      "{Advanced Death Cloud}\n\nAdvanced Death Cloud Description.\n";
   spell[SPL_DEATH_CLOUD_NEW].description[3] =
      "{Expert Death Cloud}\n\nExpert Death Cloud Description.\n";

   // Death Blow
   spell[SPL_DEATH_BLOW_NEW].type = 1;
   spell[SPL_DEATH_BLOW_NEW].wav_name = "Deathblo.wav";
   spell[SPL_DEATH_BLOW_NEW].animation_ix = ANIM_DEATH_BLOW_NEW;
   spell[SPL_DEATH_BLOW_NEW].flags = 0x1015;
   spell[SPL_DEATH_BLOW_NEW].name = "Death Blow";
   spell[SPL_DEATH_BLOW_NEW].short_name = "Death Blow";
   spell[SPL_DEATH_BLOW_NEW].level = 5;
   spell[SPL_DEATH_BLOW_NEW].school_flags = 2;
   spell[SPL_DEATH_BLOW_NEW].mana_cost[0] = 25;
   spell[SPL_DEATH_BLOW_NEW].mana_cost[1] = 20;
   spell[SPL_DEATH_BLOW_NEW].mana_cost[2] = 20;
   spell[SPL_DEATH_BLOW_NEW].mana_cost[3] = 20;
   spell[SPL_DEATH_BLOW_NEW].eff_power = 0;
   spell[SPL_DEATH_BLOW_NEW].effect[0] = 0;
   spell[SPL_DEATH_BLOW_NEW].effect[1] = 0;
   spell[SPL_DEATH_BLOW_NEW].effect[2] = 0;
   spell[SPL_DEATH_BLOW_NEW].effect[3] = 0;
   spell[SPL_DEATH_BLOW_NEW].chance2get_var[0] = 10;
   spell[SPL_DEATH_BLOW_NEW].chance2get_var[1] = 10;
   spell[SPL_DEATH_BLOW_NEW].chance2get_var[2] = 10;
   spell[SPL_DEATH_BLOW_NEW].chance2get_var[3] = 10;
   spell[SPL_DEATH_BLOW_NEW].chance2get_var[4] = 10;
   spell[SPL_DEATH_BLOW_NEW].chance2get_var[5] = 10;
   spell[SPL_DEATH_BLOW_NEW].chance2get_var[6] = 10;
   spell[SPL_DEATH_BLOW_NEW].chance2get_var[7] = 10;
   spell[SPL_DEATH_BLOW_NEW].chance2get_var[8] = 10;
   spell[SPL_DEATH_BLOW_NEW].ai_value[0] = 50;
   spell[SPL_DEATH_BLOW_NEW].ai_value[1] = 50;
   spell[SPL_DEATH_BLOW_NEW].ai_value[2] = 50;
   spell[SPL_DEATH_BLOW_NEW].ai_value[3] = 50;
   spell[SPL_DEATH_BLOW_NEW].description[0] =
      "{Death Blow}\n\nDeath Blow Description.\n";
   spell[SPL_DEATH_BLOW_NEW].description[1] =
      "{Basic Death Blow}\n\nBasic Death Blow Description.\n";
   spell[SPL_DEATH_BLOW_NEW].description[2] =
      "{Advanced Death Blow}\n\nAdvanced Death Blow Description.\n";
   spell[SPL_DEATH_BLOW_NEW].description[3] =
      "{Expert Death Blow}\n\nExpert Death Blow Description.\n";

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

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

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

   // Poison
   anim[ANIM_POISON_NEW].defName = "sp11_.def";
   anim[ANIM_POISON_NEW].name = "Poison";
   anim[ANIM_POISON_NEW].type = 1;

   // Disease
   anim[ANIM_DISEASE_NEW].defName = "sp05_.def";
   anim[ANIM_DISEASE_NEW].name = "Disease";
   anim[ANIM_DISEASE_NEW].type = 1;

   // Aging
   anim[ANIM_AGING_NEW].defName = "sp01_.def";
   anim[ANIM_AGING_NEW].name = "Aging";
   anim[ANIM_AGING_NEW].type = 1;

   // Death Cloud
   anim[ANIM_DEATH_CLOUD_NEW].defName = "sp04_.def";
   anim[ANIM_DEATH_CLOUD_NEW].name = "DeathCloud";
   anim[ANIM_DEATH_CLOUD_NEW].type = 1;

   // Death Blow
   anim[ANIM_DEATH_BLOW_NEW].defName = "sp03_.def";
   anim[ANIM_DEATH_BLOW_NEW].name = "DeathBlow";
   anim[ANIM_DEATH_BLOW_NEW].type = 1;

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

int __stdcall disableCreatureSpells(LoHook* h, HookContext* c)
{
   for (int i = SPL_STONE; i <= SPL_ACID_BREATH; ++i)
      o_GameMgr->disabled_shrines[i] = true;

   return EXEC_DEFAULT;
}

int __stdcall shrineSpellsInit(LoHook* h, HookContext* c)
{
   for (int i = SPL_STONE; i <= SPL_ACID_BREATH; ++i)
      o_GameMgr->disabled_shrines[i] = true;   
   
   memcpy(&shrineSpells, &o_GameMgr->disabled_shrines, sizeof(shrineSpells) / sizeof(bool));   

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

int __fastcall fillShrines(_GameMgr_* game, int unused_edx, int shrineFlags)
{
   int availSpellsNum = 0;

   for (int i = 0; i < SPELLS_NUM; ++i)
   {
      int lvl = o_Spell[i].level - 1;
      if (lvl < 5 && 1 << lvl & shrineFlags && o_Spell[i].school_flags && !shrineSpells[i])
         ++availSpellsNum;
   }

   int spell = 0;
   int chosenSpell = 0;

   if (availSpellsNum)
   {
      int rndSpell = Randint(0, availSpellsNum - 1);
     
      for (int i = 0; i < SPELLS_NUM; ++i)
      {
         int lvl = o_Spell[i].level - 1;
         if (lvl < 5 && 1 << lvl & shrineFlags && o_Spell[i].school_flags && !shrineSpells[i])
         {
            if (spell == rndSpell) break;
            ++spell;
         }
         ++chosenSpell;
      }
     
      shrineSpells[chosenSpell] = true;
   }
   else
   {
      spell = 0;
     
      for (int i = 0; i < SPELLS_NUM; ++i)
      {
         int lvl = o_Spell[i].level - 1;
         if (lvl < 5 && 1 << lvl & shrineFlags && o_Spell[i].school_flags && !game->disabled_shrines[i])
         {
            shrineSpells[i] = false;
            ++spell;
         }
      }
     
      chosenSpell = spell ? fillShrines(game, unused_edx, shrineFlags) : ID_NONE;
   }

   return chosenSpell; 
}

int __stdcall DeathCloud(LoHook* h, HookContext* c)
{
   if (c->edx == SPL_DEATH_CLOUD_NEW)
   {
      CALL_5(void, __thiscall, 0x5A4C80, o_BattleMgr, *(int*)(c->ebp + 0xC), SPL_DEATH_CLOUD_NEW, c->esi, *(int*)(c->ebp + 0x1C));
   
      c->return_address = 0x5A2368;
      return NO_EXEC_DEFAULT;
   }

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

         // ----------------------------------------
         // New _GameMgr_.disabled_spells[140] field
         // ----------------------------------------
         _PI->WriteByte(0x4C16ED, 4); // Pyramids
         _PI->WriteByte(0x4C254C, 4); // Titan's Lightning Bolt
         _PI->WriteByte(0x4C25F1, 4); // Titan's Lightning Bolt
         _PI->WriteByte(0x501297, 4); // Scholars
         _PI->WriteByte(0x5BEA55, 4); // Mage Guild
         
         // 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 - ADVSPELLS_NUM);
         _PI->WriteDword(0x59EFE4, (int)&spellIndirectTableA);
         _PI->WriteByte (0x5A064E, SPELLS_NUM - 1 - ADVSPELLS_NUM);
         _PI->WriteDword(0x5A0659, (int)&spellIndirectTableB);
         //_PI->WriteDword(0x4446FD, (int)&spellIndirectTableC);
         _PI->WriteDword(0x43E3DF, SPELLS_NUM);
         _PI->WriteByte (0x443F63, SPELLS_NUM);
         _PI->WriteByte (0x446F03, SPELLS_NUM);
         _PI->WriteByte (0x4963E9, ANIMS_NUM);
         _PI->WriteByte (0x4965BD, ANIMS_NUM);
         
         // --------------------------------------------------
         // New _BattleStack_.active_spell_duration[162] field
         // --------------------------------------------------
         _PI->WriteCodePatch(0x4446D2, "%n", 7);
         _PI->WriteJmp  (0x444694, 0x4450C2);
         
         // Disable creature spells
         _PI->WriteLoHook(0x4C2567, disableCreatureSpells);

         // Shrine Spells
         _PI->WriteLoHook(0x4C2625, shrineSpellsInit);
         _PI->WriteHiHook(0x4C9260, SPLICE_, DIRECT_, THISCALL_, fillShrines);
         
         // Death Cloud
         _PI->WriteLoHook(0x5A0F4C, DeathCloud);

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

   return TRUE;
}

Спецабилки теперь должны работать все. Если кто желает помочь в тестировании, интересует правильность наложения эффектов (графическая составляющая), правильность их снятия*, генерация новых заклинаний в объектах на карте и гильдии магии. Полноценно работает пока только Death Cloud.

* Не все кейсы пока найдены и пропатчены, но уже гораздо круче, чем раньше. Всё более-менее работает :smile1:

Common.zip
Распаковать в _HD3_Data\Common
(371.65 КБ) Скачиваний: 133

* * *
Цитата:
Я, думаю, на этом нам стоит остановится и не придумывать больше новых заклов, пока не реализуем все то, что уже задумали.

Плагин пока задумывается как конструктор. Новые заклинания имеет смысл добавлять после того, как всё будет работать чётко (а для этого нужно много тестов и часов с дебаггером). Поэтому пусть пока будут 6 для демонстрации. Разве Вызов Фениксов добавить. По идее, будет работать, как и другие заклинания вызова (только смотреть, что там с двухгексовостью, нужно; хотя Элементали воды нормально же вызываются).

Картинку Death Blow заменю позже (не увидел вовремя).

Сейчас планирую заниматься техническими моментами: контроль целостности структур с помощью брейкпоинтов (важно знать, например, можно ли занимать массив active_spells_power или придётся его переносить), поиск участков кода, в которых осуществляется проход по всем заклинаниям и т.п. Но это уже не в таком ударном темпе :smile1:
Вернуться к началу

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

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

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

AlexSpl писал(а):

Плагин пока задумывается как конструктор. Новые заклинания имеет смысл добавлять после того, как всё будет работать чётко (а для этого нужно много тестов и часов с дебаггером). Поэтому пусть пока будут 6 для демонстрации. Разве Вызов Фениксов добавить.

Добавьте тогда за компанию и вызов Psychic Elemental. Все равно одинаково будут делаться. Там даже проще, он 1 гексовый. Формулы и картинки я бросал. Пускай 8 будет. Их и будем тестировать и вылизывать. Потом как все будет работать, можно будет попробовать из MoP хотя бы одно походное перетащить, Сбор войск, например.

AlexSpl писал(а):

Если кто желает помочь в тестировании, интересует правильность наложения эффектов (графическая составляющая), правильность их снятия

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

Пред.След.

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

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

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

cron