-
AlexSpl
имя: Александр
- Эксперт
-
- Сообщения: 5587
- Зарегистрирован: 17 сен 2010, 12:58
- Пол:
- Награды: 14
-
-
- Поблагодарили: 2185 раз.
|
AlexSpl » 03 сен 2021, 14:42
Наладил взаимодействие между заклинаниями, влияющими на скорость. Теперь всё, как в оригинале. Пример. Герой с силой магии 5 (без артефактов) вешает в 1-й раунд Expert Fear на отряд Master Genies (скорость 11). Скорость падает до [11 - 75%] = 2. Во 2-м раунде герой кастует Expert Slow, скорость остаётся равной 2, т.к. [11 - 50%] > [11 - 75%]. В 3-м раунде герой кастует Expert Disease, скорость по-прежнему 2, т.к. [11 - 40%] > [11 - 75%]. В 6-м раунде снимается Expert Fear. Следующий по силе эффект, влияющий на скорость, - Expert Slow. Скорость отряда Master Genie становится равной [11 - 50%] = 5. В 7-м раунде пропадает Expert Slow, но т.к. продолжает висеть Expert Disease, то скорость отряда равна [11 - 40%] = 6. В 8-м раунде скорость отряда Master Genie возвращается к первоначальной (11). Вот код для тестирования других взаимодействий заклинаний, влияющих на скорость (например, в связке с Prayer и Haste): NewSpells - Код: Выделить всё
#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 89 #define SPL_FEAR_NEW 81 #define SPL_POISON_NEW 82 #define SPL_DISEASE_NEW 83 #define SPL_AGE_NEW 84 #define SPL_DEATH_CLOUD_NEW 85 #define SPL_DEATH_BLOW_NEW 86 #define SPL_FIREBIRD_NEW 87 #define SPL_MAGIC_ELEMENTAL_NEW 88
#define ANIMS_NUM 89 #define ANIM_FEAR_NEW 83 #define ANIM_POISON_NEW 84 #define ANIM_DISEASE_NEW 85 #define ANIM_AGE_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];
// These are not exactly optional tables, so don't remove them 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, 10, 10 };
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, 38, 39 };
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, 33, 32, 32, 32, 32, 32 };
char spellIndirectTableD[] = { // Spells (starting from Weakness #45) 0, 1, 9, 2, 9, 9, 9, 9, 3, 4, 9, 9, 9, 9, 9, 5, 9, 9, 9, 9, 9, 9, 9, 9, 9, // Special Abilities 9, 9, 6, 7, 9, 8, -1, -1, -1, -1, -1, // New Spells (starting from Fear #81) 9, 9, 10, 9, 9, 9, 9, 9 };
char spellIndirectTableE[] = { // Spells (starting from Lightning Bolt #17) 0, 16, 0, 16, 16, 16, 16, 1, 2, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 3, 4, 16, 5, 6, 16, 7, 16, 16, 16, 16, 8, 8, 9, 9, 16, 16, 9, 16, 16, 16, 10, 11, 12, 13, 16, 16, 16, 16, 16, 16, 16, // Special Abilities 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // New Spells (starting from Fear #81) 16, 16, 16, 16, 16, 16, 16, 16 };
bool shrineSpells[SPELLS_MAX];
struct FearSpellParams { int SpeedMod; };
FearSpellParams fearSpellParams[2][21];
struct DiseaseSpellParams { int AttackPenalty; int DefensePenalty; int SpeedMod; };
DiseaseSpellParams diseaseSpell[4]; DiseaseSpellParams diseaseSpellParams[2][21];
struct SlowSpellParams { int SpeedMod; };
SlowSpellParams slowSpellParams[2][21];
struct AgeSpellParams { int HealthMod; };
AgeSpellParams ageSpellParams[2][21];
// Debug void showDebugInfo(const char* format, ...) { va_list args; va_start(args, format);
vsprintf(o_TextBuffer, format, args); b_MsgBox(o_TextBuffer, MBX_OK); va_end(args); }
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 = SSF_EARTH; 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] = 100; spell[SPL_FEAR_NEW].ai_value[3] = 150; 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 = SSF_EARTH; 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] = 100; spell[SPL_POISON_NEW].ai_value[3] = 150; 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 = 0x40045; 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 = SSF_EARTH; 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] = 80; spell[SPL_DISEASE_NEW].effect[1] = 80; spell[SPL_DISEASE_NEW].effect[2] = 60; spell[SPL_DISEASE_NEW].effect[3] = 60; spell[SPL_DISEASE_NEW].chance2get_var[0] = 0; spell[SPL_DISEASE_NEW].chance2get_var[1] = 0; spell[SPL_DISEASE_NEW].chance2get_var[2] = 0; spell[SPL_DISEASE_NEW].chance2get_var[3] = 5; spell[SPL_DISEASE_NEW].chance2get_var[4] = 10; spell[SPL_DISEASE_NEW].chance2get_var[5] = 5; spell[SPL_DISEASE_NEW].chance2get_var[6] = 0; spell[SPL_DISEASE_NEW].chance2get_var[7] = 0; spell[SPL_DISEASE_NEW].chance2get_var[8] = 5; spell[SPL_DISEASE_NEW].ai_value[0] = 50; spell[SPL_DISEASE_NEW].ai_value[1] = 50; spell[SPL_DISEASE_NEW].ai_value[2] = 100; spell[SPL_DISEASE_NEW].ai_value[3] = 150; 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";
diseaseSpell[0].AttackPenalty = 2; diseaseSpell[0].DefensePenalty = 2; diseaseSpell[0].SpeedMod = 20; diseaseSpell[1].AttackPenalty = 2; diseaseSpell[1].DefensePenalty = 2; diseaseSpell[1].SpeedMod = 20; diseaseSpell[2].AttackPenalty = 4; diseaseSpell[2].DefensePenalty = 4; diseaseSpell[2].SpeedMod = 40; diseaseSpell[3].AttackPenalty = 4; diseaseSpell[3].DefensePenalty = 4; diseaseSpell[3].SpeedMod = 40; // Aging spell[SPL_AGE_NEW].type = -1; spell[SPL_AGE_NEW].wav_name = "Age.wav"; spell[SPL_AGE_NEW].animation_ix = ANIM_AGE_NEW; spell[SPL_AGE_NEW].flags = 0x1015; spell[SPL_AGE_NEW].name = "Age"; spell[SPL_AGE_NEW].short_name = "Age"; spell[SPL_AGE_NEW].level = 5; spell[SPL_AGE_NEW].school_flags = SSF_FIRE; spell[SPL_AGE_NEW].mana_cost[0] = 25; spell[SPL_AGE_NEW].mana_cost[1] = 20; spell[SPL_AGE_NEW].mana_cost[2] = 20; spell[SPL_AGE_NEW].mana_cost[3] = 20; spell[SPL_AGE_NEW].eff_power = 0; spell[SPL_AGE_NEW].effect[0] = 70; spell[SPL_AGE_NEW].effect[1] = 70; spell[SPL_AGE_NEW].effect[2] = 60; spell[SPL_AGE_NEW].effect[3] = 50; spell[SPL_AGE_NEW].chance2get_var[0] = 10; spell[SPL_AGE_NEW].chance2get_var[1] = 10; spell[SPL_AGE_NEW].chance2get_var[2] = 10; spell[SPL_AGE_NEW].chance2get_var[3] = 10; spell[SPL_AGE_NEW].chance2get_var[4] = 10; spell[SPL_AGE_NEW].chance2get_var[5] = 10; spell[SPL_AGE_NEW].chance2get_var[6] = 10; spell[SPL_AGE_NEW].chance2get_var[7] = 10; spell[SPL_AGE_NEW].chance2get_var[8] = 10; spell[SPL_AGE_NEW].ai_value[0] = 50; spell[SPL_AGE_NEW].ai_value[1] = 50; spell[SPL_AGE_NEW].ai_value[2] = 100; spell[SPL_AGE_NEW].ai_value[3] = 150; spell[SPL_AGE_NEW].description[0] = "{Age}\n\nAge Description.\n"; spell[SPL_AGE_NEW].description[1] = "{Basic Age}\n\nBasic Age Description.\n"; spell[SPL_AGE_NEW].description[2] = "{Advanced Age}\n\nAdvanced Age Description.\n"; spell[SPL_AGE_NEW].description[3] = "{Expert Age}\n\nExpert Age 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 = SSF_EARTH; 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] = 100; spell[SPL_DEATH_CLOUD_NEW].ai_value[3] = 150; 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 = SSF_FIRE; 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] = 100; spell[SPL_DEATH_BLOW_NEW].ai_value[3] = 150; 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";
// Firebird spell[SPL_FIREBIRD_NEW].type = 0; spell[SPL_FIREBIRD_NEW].wav_name = "SumnElm.wav"; spell[SPL_FIREBIRD_NEW].animation_ix = ID_NONE; spell[SPL_FIREBIRD_NEW].flags = 0x80001; spell[SPL_FIREBIRD_NEW].name = "Firebird"; spell[SPL_FIREBIRD_NEW].short_name = "Summon Firebird"; spell[SPL_FIREBIRD_NEW].level = 5; spell[SPL_FIREBIRD_NEW].school_flags = SSF_FIRE; spell[SPL_FIREBIRD_NEW].mana_cost[0] = 30; spell[SPL_FIREBIRD_NEW].mana_cost[1] = 25; spell[SPL_FIREBIRD_NEW].mana_cost[2] = 25; spell[SPL_FIREBIRD_NEW].mana_cost[3] = 25; spell[SPL_FIREBIRD_NEW].eff_power = 0; spell[SPL_FIREBIRD_NEW].effect[0] = 50; spell[SPL_FIREBIRD_NEW].effect[1] = 50; spell[SPL_FIREBIRD_NEW].effect[2] = 75; spell[SPL_FIREBIRD_NEW].effect[3] = 100; spell[SPL_FIREBIRD_NEW].chance2get_var[0] = 10; spell[SPL_FIREBIRD_NEW].chance2get_var[1] = 10; spell[SPL_FIREBIRD_NEW].chance2get_var[2] = 10; spell[SPL_FIREBIRD_NEW].chance2get_var[3] = 10; spell[SPL_FIREBIRD_NEW].chance2get_var[4] = 10; spell[SPL_FIREBIRD_NEW].chance2get_var[5] = 10; spell[SPL_FIREBIRD_NEW].chance2get_var[6] = 10; spell[SPL_FIREBIRD_NEW].chance2get_var[7] = 10; spell[SPL_FIREBIRD_NEW].chance2get_var[8] = 10; spell[SPL_FIREBIRD_NEW].ai_value[0] = 50; spell[SPL_FIREBIRD_NEW].ai_value[1] = 50; spell[SPL_FIREBIRD_NEW].ai_value[2] = 100; spell[SPL_FIREBIRD_NEW].ai_value[3] = 150; spell[SPL_FIREBIRD_NEW].description[0] = "{Summon Firebird}\n\nAllows you to summon firebirds. Once cast, no other elemental types may be summoned.\n"; spell[SPL_FIREBIRD_NEW].description[1] = "{Basic Summon Firebird}\n\nAllows you to summon firebirds. Once cast, no other elemental types may be summoned.\n"; spell[SPL_FIREBIRD_NEW].description[2] = "{Advanced Summon Firebird}\n\nAllows you to summon firebirds. Once cast, no other elemental types may be summoned.\n\n" \ "Summons approximately one-and-a-half times the number of units as Basic Summon Firebird.\n"; spell[SPL_FIREBIRD_NEW].description[3] = "{Expert Summon Firebird}\n\nAllows you to summon firebirds. Once cast, no other elemental types may be summoned.\n\n" \ "Summons approximately twice the number of units as Basic Summon Firebird.\n";
// Magic Elemental spell[SPL_MAGIC_ELEMENTAL_NEW].type = 0; spell[SPL_MAGIC_ELEMENTAL_NEW].wav_name = "SumnElm.wav"; spell[SPL_MAGIC_ELEMENTAL_NEW].animation_ix = ID_NONE; spell[SPL_MAGIC_ELEMENTAL_NEW].flags = 0x80001; spell[SPL_MAGIC_ELEMENTAL_NEW].name = "Magic Elemental"; spell[SPL_MAGIC_ELEMENTAL_NEW].short_name = "Summon Magic Elemental"; spell[SPL_MAGIC_ELEMENTAL_NEW].level = 5; spell[SPL_MAGIC_ELEMENTAL_NEW].school_flags = SSF_AIR | SSF_FIRE | SSF_WATER | SSF_EARTH; spell[SPL_MAGIC_ELEMENTAL_NEW].mana_cost[0] = 30; spell[SPL_MAGIC_ELEMENTAL_NEW].mana_cost[1] = 25; spell[SPL_MAGIC_ELEMENTAL_NEW].mana_cost[2] = 25; spell[SPL_MAGIC_ELEMENTAL_NEW].mana_cost[3] = 25; spell[SPL_MAGIC_ELEMENTAL_NEW].eff_power = 0; spell[SPL_MAGIC_ELEMENTAL_NEW].effect[0] = 100; spell[SPL_MAGIC_ELEMENTAL_NEW].effect[1] = 100; spell[SPL_MAGIC_ELEMENTAL_NEW].effect[2] = 150; spell[SPL_MAGIC_ELEMENTAL_NEW].effect[3] = 200; spell[SPL_MAGIC_ELEMENTAL_NEW].chance2get_var[0] = 10; spell[SPL_MAGIC_ELEMENTAL_NEW].chance2get_var[1] = 10; spell[SPL_MAGIC_ELEMENTAL_NEW].chance2get_var[2] = 10; spell[SPL_MAGIC_ELEMENTAL_NEW].chance2get_var[3] = 10; spell[SPL_MAGIC_ELEMENTAL_NEW].chance2get_var[4] = 10; spell[SPL_MAGIC_ELEMENTAL_NEW].chance2get_var[5] = 10; spell[SPL_MAGIC_ELEMENTAL_NEW].chance2get_var[6] = 10; spell[SPL_MAGIC_ELEMENTAL_NEW].chance2get_var[7] = 10; spell[SPL_MAGIC_ELEMENTAL_NEW].chance2get_var[8] = 10; spell[SPL_MAGIC_ELEMENTAL_NEW].ai_value[0] = 50; spell[SPL_MAGIC_ELEMENTAL_NEW].ai_value[1] = 50; spell[SPL_MAGIC_ELEMENTAL_NEW].ai_value[2] = 100; spell[SPL_MAGIC_ELEMENTAL_NEW].ai_value[3] = 150; spell[SPL_MAGIC_ELEMENTAL_NEW].description[0] = "{Summon Magic Elemental}\n\nAllows you to summon magic elementals. Once cast, no other elemental types may be summoned.\n"; spell[SPL_MAGIC_ELEMENTAL_NEW].description[1] = "{Basic Summon Magic Elemental}\n\nAllows you to summon magic elementals. Once cast, no other elemental types may be summoned.\n"; spell[SPL_MAGIC_ELEMENTAL_NEW].description[2] = "{Advanced Summon Magic Elemental}\n\nAllows you to summon magic elementals. Once cast, no other elemental types may be summoned.\n\n" \ "Summons approximately one-and-a-half times the number of units as Basic Summon Magic Elemental.\n"; spell[SPL_MAGIC_ELEMENTAL_NEW].description[3] = "{Expert Summon Magic Elemental}\n\nAllows you to summon magic elementals. Once cast, no other elemental types may be summoned.\n\n" \ "Summons approximately twice the number of units as Basic Summon Magic Elemental.\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_AGE_NEW].defName = "sp01_.def"; anim[ANIM_AGE_NEW].name = "Age"; anim[ANIM_AGE_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 skipTownSetupForNewSpells(LoHook* h, HookContext* c) { int spell = c->edi;
if (spell > SPL_AIR_ELEMENTAL) { c->return_address = 0x5BEA4C; return NO_EXEC_DEFAULT; }
return EXEC_DEFAULT; }
int __stdcall disableCreatureSpells(LoHook* h, HookContext* c) { for (int i = SPL_FEAR_NEW; i < SPELLS_NUM; ++i) o_GameMgr->disabled_shrines[i] = false;
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_FEAR_NEW; i < SPELLS_NUM; ++i) o_GameMgr->disabled_shrines[i] = false;
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 fillShrine(_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 ? fillShrine(game, unused_edx, shrineFlags) : ID_NONE; }
return chosenSpell; }
bool __fastcall unitCanAttack(_BattleStack_* stack, int unused_edx, _BattleStack_* foeStack) { if (!foeStack || stack == foeStack) return false;
if (stack->active_spell_duration[SPL_BERSERK] || foeStack->active_spell_duration[SPL_BERSERK]) return true;
int side = stack->active_spell_duration[SPL_HYPNOTIZE] ? 1 - stack->side : stack->side;
return side != foeStack->side; }
int __stdcall setCursorForFearSpell(HiHook* h, _BattleMgr_* battleMgr, int hex) { int cursorType = CALL_2(int, __thiscall, h->GetDefaultFunc(), battleMgr, hex);
_BattleStack_* stack = &battleMgr->stack[battleMgr->current_side][battleMgr->current_stack_ix];
if (stack && stack->active_spell_duration[SPL_FEAR_NEW] && (cursorType == 3 || cursorType == 15 || cursorType == 7)) return 0;
return cursorType; }
int __stdcall initSpeedMods(LoHook* h, HookContext* c) { for (int side = ATTACKER; side <= DEFENDER; ++side) for (int i = 0; i < 21; ++i) { o_BattleMgr->stack[side][i].Field<float>(0x4C8) = 1.0f; slowSpellParams[side][i].SpeedMod = 100; fearSpellParams[side][i].SpeedMod = 100; diseaseSpellParams[side][i].SpeedMod = 100; } return EXEC_DEFAULT; }
int __stdcall applySpell(LoHook* h, HookContext* c) { int spell = *(int*)(c->ebp + 8); _Hero_* hero = *(_Hero_**)(c->ebp + 0x14); _BattleStack_* stack = (_BattleStack_*)c->esi; int schoolLevel = c->eax; int spellSpecialtyEffect = 0; switch (spell) { case SPL_FEAR_NEW: // showDebugInfo("Speed = %d", stack->creature.speed);
if (!(stack->creature.flags & BCF_CANT_MOVE)) { fearSpellParams[stack->side][stack->index_on_side].SpeedMod = o_Spell[SPL_FEAR_NEW].effect[schoolLevel];
int minSpeedMod = 100;
if (stack->active_spell_duration[SPL_SLOW] && slowSpellParams[stack->side][stack->index_on_side].SpeedMod < minSpeedMod) minSpeedMod = slowSpellParams[stack->side][stack->index_on_side].SpeedMod;
if (stack->active_spell_duration[SPL_DISEASE_NEW] && diseaseSpellParams[stack->side][stack->index_on_side].SpeedMod < minSpeedMod) minSpeedMod = diseaseSpellParams[stack->side][stack->index_on_side].SpeedMod;
if (fearSpellParams[stack->side][stack->index_on_side].SpeedMod < minSpeedMod) minSpeedMod = fearSpellParams[stack->side][stack->index_on_side].SpeedMod; stack->Field<float>(0x4C8) = minSpeedMod / 100.0f; } break;
case SPL_DISEASE_NEW: // Init diseaseSpellParams[stack->side][stack->index_on_side].AttackPenalty = diseaseSpell[schoolLevel].AttackPenalty; diseaseSpellParams[stack->side][stack->index_on_side].DefensePenalty = diseaseSpell[schoolLevel].DefensePenalty; // Optional, as we don't have heroes with Disease spell specialty yet if (hero) { spellSpecialtyEffect = CALL_4(int, __thiscall, 0x4E6260, hero, SPL_DISEASE_NEW, hero->level, 0); diseaseSpellParams[stack->side][stack->index_on_side].AttackPenalty += spellSpecialtyEffect; diseaseSpellParams[stack->side][stack->index_on_side].DefensePenalty += spellSpecialtyEffect; }
stack->creature.attack -= diseaseSpellParams[stack->side][stack->index_on_side].AttackPenalty; stack->creature.defence -= diseaseSpellParams[stack->side][stack->index_on_side].DefensePenalty; // Not allowing attack and defense to drop below 0 if (stack->creature.attack < 0) { diseaseSpellParams[stack->side][stack->index_on_side].AttackPenalty += stack->creature.attack; stack->creature.attack = 0; }
if (stack->creature.defence < 0) { diseaseSpellParams[stack->side][stack->index_on_side].DefensePenalty += stack->creature.defence; stack->creature.defence = 0; }
if (!(stack->creature.flags & BCF_CANT_MOVE)) { diseaseSpellParams[stack->side][stack->index_on_side].SpeedMod = o_Spell[SPL_DISEASE_NEW].effect[schoolLevel] + spellSpecialtyEffect;
int minSpeedMod = 100;
if (stack->active_spell_duration[SPL_SLOW] && slowSpellParams[stack->side][stack->index_on_side].SpeedMod < minSpeedMod) minSpeedMod = slowSpellParams[stack->side][stack->index_on_side].SpeedMod;
if (stack->active_spell_duration[SPL_FEAR_NEW] && fearSpellParams[stack->side][stack->index_on_side].SpeedMod < minSpeedMod) minSpeedMod = fearSpellParams[stack->side][stack->index_on_side].SpeedMod;
if (diseaseSpellParams[stack->side][stack->index_on_side].SpeedMod < minSpeedMod) minSpeedMod = diseaseSpellParams[stack->side][stack->index_on_side].SpeedMod; stack->Field<float>(0x4C8) = minSpeedMod / 100.0f; } break;
case SPL_SLOW: if (!(stack->creature.flags & BCF_CANT_MOVE)) { // Removing Haste spell CALL_2(void, __thiscall, 0x444230, stack, SPL_HASTE); slowSpellParams[stack->side][stack->index_on_side].SpeedMod = *(int*)(c->ebp + 0x10);
int minSpeedMod = 100;
if (stack->active_spell_duration[SPL_FEAR_NEW] && fearSpellParams[stack->side][stack->index_on_side].SpeedMod < minSpeedMod) minSpeedMod = fearSpellParams[stack->side][stack->index_on_side].SpeedMod;
if (stack->active_spell_duration[SPL_DISEASE_NEW] && diseaseSpellParams[stack->side][stack->index_on_side].SpeedMod < minSpeedMod) minSpeedMod = diseaseSpellParams[stack->side][stack->index_on_side].SpeedMod;
if (slowSpellParams[stack->side][stack->index_on_side].SpeedMod < minSpeedMod) minSpeedMod = slowSpellParams[stack->side][stack->index_on_side].SpeedMod; stack->Field<float>(0x4C8) = minSpeedMod / 100.0f;
// Slow animation stack->Field<int>(0x158) = (int)(stack->Field<int>(0x68) * 1.5); } break;
case SPL_AGING: case SPL_AGE_NEW: { float healthMul = stack->Field<float>(0x4A4); double fullHealth = (double)stack->full_hp * healthMul;
int minHealthMod = 100;
if (stack->active_spell_duration[SPL_AGE_NEW]) { ageSpellParams[stack->side][stack->index_on_side].HealthMod = o_Spell[SPL_AGE_NEW].effect[schoolLevel]; if (ageSpellParams[stack->side][stack->index_on_side].HealthMod < minHealthMod) minHealthMod = ageSpellParams[stack->side][stack->index_on_side].HealthMod; }
if (stack->active_spell_duration[SPL_AGING] && 50 < minHealthMod) minHealthMod = 50;
fullHealth *= minHealthMod / 100.0;
int health = (int)(fullHealth + 0.95);
stack->creature.hit_points = health;
if (health - 1 < stack->lost_hp) stack->lost_hp = health - 1; }
break;
default: return EXEC_DEFAULT; } c->return_address = 0x444D5C; return NO_EXEC_DEFAULT; }
int __stdcall resetSpell(LoHook* h, HookContext* c) { int spell = *(int*)(c->ebp + 8); _BattleStack_* stack = (_BattleStack_*)c->esi;
switch (spell) { case SPL_FEAR_NEW: if (!(stack->creature.flags & BCF_CANT_MOVE)) { int minSpeedMod = 100;
if (stack->active_spell_duration[SPL_SLOW] && slowSpellParams[stack->side][stack->index_on_side].SpeedMod < minSpeedMod) minSpeedMod = slowSpellParams[stack->side][stack->index_on_side].SpeedMod;
if (stack->active_spell_duration[SPL_DISEASE_NEW] && diseaseSpellParams[stack->side][stack->index_on_side].SpeedMod < minSpeedMod) minSpeedMod = diseaseSpellParams[stack->side][stack->index_on_side].SpeedMod;
stack->Field<float>(0x4C8) = minSpeedMod / 100.0f; }
fearSpellParams[stack->side][stack->index_on_side].SpeedMod = 100; break;
case SPL_DISEASE_NEW: stack->creature.attack += diseaseSpellParams[stack->side][stack->index_on_side].AttackPenalty; stack->creature.defence += diseaseSpellParams[stack->side][stack->index_on_side].DefensePenalty;
if (!(stack->creature.flags & BCF_CANT_MOVE)) { int minSpeedMod = 100;
if (stack->active_spell_duration[SPL_SLOW] && slowSpellParams[stack->side][stack->index_on_side].SpeedMod < minSpeedMod) minSpeedMod = slowSpellParams[stack->side][stack->index_on_side].SpeedMod;
if (stack->active_spell_duration[SPL_FEAR_NEW] && fearSpellParams[stack->side][stack->index_on_side].SpeedMod < minSpeedMod) minSpeedMod = fearSpellParams[stack->side][stack->index_on_side].SpeedMod;
stack->Field<float>(0x4C8) = minSpeedMod / 100.0f; } diseaseSpellParams[stack->side][stack->index_on_side].SpeedMod = 100; break; case SPL_SLOW: if (!(stack->creature.flags & BCF_CANT_MOVE)) { int minSpeedMod = 100;
if (stack->active_spell_duration[SPL_FEAR_NEW] && fearSpellParams[stack->side][stack->index_on_side].SpeedMod < minSpeedMod) minSpeedMod = fearSpellParams[stack->side][stack->index_on_side].SpeedMod;
if (stack->active_spell_duration[SPL_DISEASE_NEW] && diseaseSpellParams[stack->side][stack->index_on_side].SpeedMod < minSpeedMod) minSpeedMod = diseaseSpellParams[stack->side][stack->index_on_side].SpeedMod;
stack->Field<float>(0x4C8) = minSpeedMod / 100.0f; }
// Slow animation stack->Field<int>(0x158) = stack->Field<int>(0x68);
slowSpellParams[stack->side][stack->index_on_side].SpeedMod = 100; break;
case SPL_AGING: case SPL_AGE_NEW: { float healthMul = stack->Field<float>(0x4A4); double fullHealth = (double)stack->full_hp * healthMul;
int minHealthMod = 100;
if (stack->active_spell_duration[SPL_AGE_NEW] && ageSpellParams[stack->side][stack->index_on_side].HealthMod < minHealthMod) minHealthMod = ageSpellParams[stack->side][stack->index_on_side].HealthMod;
if (stack->active_spell_duration[SPL_AGING] && 50 < minHealthMod) minHealthMod = 50;
fullHealth *= minHealthMod / 100.0;
int health = (int)(fullHealth + 0.95);
stack->creature.hit_points = health;
if (health - 1 < stack->lost_hp) stack->lost_hp = health - 1; }
break; default: return EXEC_DEFAULT; }
c->return_address = 0x4444E1; return NO_EXEC_DEFAULT; }
int __stdcall cureNewSpells(LoHook* h, HookContext* c) { _BattleStack_* stack = (_BattleStack_*)c->esi;
CALL_2(void, __thiscall, 0x444230, stack, SPL_FEAR_NEW); CALL_2(void, __thiscall, 0x444230, stack, SPL_POISON_NEW); CALL_2(void, __thiscall, 0x444230, stack, SPL_DISEASE_NEW); CALL_2(void, __thiscall, 0x444230, stack, SPL_AGE_NEW);
return EXEC_DEFAULT; }
int __stdcall getStackMagicVulnerability(LoHook* h, HookContext* c) { _CreatureInfo_* creature = (_CreatureInfo_*)c->edi; int creatureId = *(int*)(c->ebp - 8); int spell = *(int*)(c->ebp - 0x10);
bool immune = false;
switch (spell) { case SPL_FEAR_NEW: if (!(creature->flags & BCF_ALIVE)) immune = true; break; case SPL_POISON_NEW: if (!(creature->flags & BCF_ALIVE) || creatureId == CID_STONE_GARGOYLE || creatureId == CID_OBSIDIAN_GARGOYLE) immune = true; break; // Let's allow to cast Disease on battle machines case SPL_DISEASE_NEW: if (!(creature->flags & BCF_ALIVE) && !(creature->flags & BCF_CANT_MOVE)) immune = true; break; case SPL_AGE_NEW: if (!(creature->flags & BCF_ALIVE)) immune = true; break; default: return EXEC_DEFAULT; }
c->return_address = immune ? 0x44A3E8 : 0x44A416; return NO_EXEC_DEFAULT; }
// ------------------ // AI Spell Weighting // ------------------ struct type_AI_spellcaster : _Struct_ { };
struct type_enchant_data : _Struct_ { int a1; int a2; int a3; int a4; int a5; };
int get_total_combat_value(_BattleStack_* stack, int a1, int a2) { return CALL_3(int, __thiscall, 0x442B80, stack, a1, a2); }
int __fastcall AI_Weight_Fear(type_AI_spellcaster* caster, int unused_edx, _BattleStack_* stack, type_enchant_data data) { return 10000; }
int __fastcall AI_Weight_Poison(type_AI_spellcaster* caster, int unused_edx, _BattleStack_* stack, type_enchant_data data) { return 10000; }
int __fastcall AI_Weight_Disease(type_AI_spellcaster* caster, int unused_edx, _BattleStack_* stack, type_enchant_data data) { return 10000; }
int __fastcall AI_Weight_Age(type_AI_spellcaster* caster, int unused_edx, _BattleStack_* stack, type_enchant_data data) { int value; if (caster->Field<char>(0x1C)) value = 0; else value = get_total_combat_value(stack, caster->Field<int>(0x20), caster->Field<int>(0x24)) / 3;
//sprintf(o_TextBuffer, "{%s}\n\n%08X %d %d %d %d", stack->creature.name_plural, caster, // caster->Field<char>(0x1C), caster->Field<int>(0x20), caster->Field<int>(0x24), value); //b_MsgBox(o_TextBuffer, MBX_OK);
return value; }
int __fastcall AI_Weight_DeathCloud(type_AI_spellcaster* caster, int unused_edx, _BattleStack_* stack, type_enchant_data data) { return 10000; }
int __fastcall AI_Weight_DeathBlow(type_AI_spellcaster* caster, int unused_edx, _BattleStack_* stack, type_enchant_data data) { return 10000; }
int __fastcall AI_Weight_SummonFirebird(type_AI_spellcaster* caster, int unused_edx, _BattleStack_* stack, type_enchant_data data) { return 10000; }
int __fastcall AI_Weight_SummonMagicElemental(type_AI_spellcaster* caster, int unused_edx, _BattleStack_* stack, type_enchant_data data) { return 10000; }
int __stdcall getSpellWeight(HiHook* h, _BattleMgr_* battleMgr, int spell) { switch (spell) { case SPL_FEAR_NEW: return (int)AI_Weight_Fear; break; case SPL_POISON_NEW: return (int)AI_Weight_Poison; break; case SPL_DISEASE_NEW: return (int)AI_Weight_Disease; break; case SPL_AGE_NEW: return (int)AI_Weight_Age; break; case SPL_DEATH_CLOUD_NEW: return (int)AI_Weight_DeathCloud; break; case SPL_DEATH_BLOW_NEW: return (int)AI_Weight_DeathBlow; break; case SPL_FIREBIRD_NEW: return (int)AI_Weight_SummonFirebird; break; case SPL_MAGIC_ELEMENTAL_NEW: return (int)AI_Weight_SummonMagicElemental; break; default: return CALL_2(int, __thiscall, h->GetDefaultFunc(), battleMgr, spell); } }
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; }
int __stdcall SummonCreatures(LoHook* h, HookContext* c) { if (c->ecx == 38 || c->ecx == 39) { int spell; int creature;
switch (c->ecx) { case 38: spell = SPL_FIREBIRD_NEW; creature = CID_FIREBIRD; break; case 39: spell = SPL_MAGIC_ELEMENTAL_NEW; creature = CID_MAGIC_ELEMENTAL; break; }
CALL_5(void, __thiscall, 0x5A7390, o_BattleMgr, spell, creature, *(int*)(c->ebp + 0x1C), c->esi); c->return_address = 0x5A2368; return NO_EXEC_DEFAULT; }
return EXEC_DEFAULT; }
int __stdcall setSummonedCreaturesNumber(LoHook* h, HookContext* c) { int creature = *(int*)(c->ebp + 0xC); int sp = *(int*)(c->ebp + 0x10); int schoolLevel = *(int*)(c->ebp + 0x14);
if (creature == CID_FIREBIRD) c->esi = o_Spell[SPL_FIREBIRD_NEW].effect[schoolLevel] * sp / 100; else if (creature == CID_MAGIC_ELEMENTAL) c->esi = o_Spell[SPL_MAGIC_ELEMENTAL_NEW].effect[schoolLevel] * sp / 100; else return EXEC_DEFAULT;
if (c->esi < 1) c->esi = 1; c->return_address = 0x5A7526; return NO_EXEC_DEFAULT; }
int __stdcall checkSummonedCreaturesType(LoHook* h, HookContext* c) { int spell = c->ebx;
switch (spell) { case SPL_FIRE_ELEMENTAL: c->eax = CID_FIRE_ELEMENTAL; break; case SPL_EARTH_ELEMENTAL: c->eax = CID_EARTH_ELEMENTAL; break; case SPL_WATER_ELEMENTAL: c->eax = CID_WATER_ELEMENTAL; break; case SPL_AIR_ELEMENTAL: c->eax = CID_AIR_ELEMENTAL; break; case SPL_FIREBIRD_NEW: c->eax = CID_FIREBIRD; break; case SPL_MAGIC_ELEMENTAL_NEW: c->eax = CID_MAGIC_ELEMENTAL; break; default: c->eax = ID_NONE; } c->return_address = 0x59F8B7; return NO_EXEC_DEFAULT; }
int __stdcall skipMeleeAttackUnderFear(LoHook* h, HookContext* c) { _BattleStack_* stack = o_BattleMgr->GetCurrentStack(); if (stack && stack->active_spell_duration[SPL_FEAR_NEW]) { c->return_address = 0x4220B3; return NO_EXEC_DEFAULT; }
return EXEC_DEFAULT; }
int __stdcall skipShootingUnderFear(LoHook* h, HookContext* c) { _BattleStack_* stack = o_BattleMgr->GetCurrentStack(); if (stack && stack->active_spell_duration[SPL_FEAR_NEW]) { c->return_address = 0x41F263; 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_)); _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_)); // ------------ // Adventure AI // ------------ _PI->WriteDword(0x430AE5, 0x3EA + SPL_DIMENSION_DOOR); _PI->WriteDword(0x4E57CC, 0x3EA + SPL_SUMMON_BOAT); _PI->WriteDword(0x56B346, 0x3EA + SPL_TOWN_PORTAL); _PI->WriteDword(0x56B7EA, 0x3EA + SPL_DIMENSION_DOOR); _PI->WriteDword(0x56B93F, 0x3EA + SPL_FLY); _PI->WriteDword(0x56B99A, 0x3EA + SPL_WATER_WALK); // --------- // Battle AI // --------- _PI->WriteDword(0x4397E6, SPELLS_NUM * sizeof(_Spell_)); _PI->WriteDword(0x43C6F2, SPELLS_NUM * sizeof(_Spell_)); // Master Genie AI Spell Weighting _PI->WriteDword(0x43C21B, SPELLS_NUM * sizeof(_Spell_)); // AI Quick Battle _PI->WriteDword(0x433026, 0x3EA); _PI->WriteDword(0x433719, 0x3EA); _PI->WriteDword(0x43940C, 0x3EA); _PI->WriteDword(0x43C561, 0x3EA); // AI Spell Scrolls? _PI->WriteDword(0x52A97A, 0x3EA); _PI->WriteDword(0x52A9B6, 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); // Scholar Secondary Skill _PI->WriteByte (0x4A2743, 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_));
// 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); _PI->WriteLoHook(0x5BEA36, skipTownSetupForNewSpells); // Temp // 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 _PI->WriteDword(0x52AE1C, 0x3EA);
// ------ // Battle // ------ _PI->WriteByte (0x4963E9, ANIMS_NUM); _PI->WriteByte (0x4965BD, ANIMS_NUM); _PI->WriteByte (0x502405, SPELLS_NUM); _PI->WriteByte (0x502451, SPELLS_NUM); _PI->WriteByte (0x5024CF, SPELLS_NUM); _PI->WriteByte (0x502515, SPELLS_NUM); _PI->WriteByte (0x502E62, SPELLS_NUM); _PI->WriteByte (0x502EB7, SPELLS_NUM); _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->WriteByte (0x4446EA, SPELLS_NUM - 1 - SPL_SHIELD); _PI->WriteDword(0x4446FD, (int)&spellIndirectTableC); _PI->WriteByte (0x444262, SPELLS_NUM - 1 - SPL_WEAKNESS); _PI->WriteDword(0x44427A, (int)&spellIndirectTableD); _PI->WriteByte (0x44A259, SPELLS_NUM - 1 - SPL_LIGHTNING_BOLT); _PI->WriteDword(0x44A264, (int)&spellIndirectTableE); // -------------------------------------------------- // New _BattleStack_.active_spell_duration[162] field // -------------------------------------------------- _PI->WriteDword(0x43787A, 162); _PI->WriteCodePatch(0x437880, "%n", 19); _PI->WriteDword(0x43D314, 162); _PI->WriteDword(0x43E3DF, SPELLS_NUM); _PI->WriteByte (0x443F63, SPELLS_NUM); _PI->WriteByte (0x446F03, SPELLS_NUM); _PI->WriteByte (0x5A1907, SPELLS_NUM); _PI->WriteByte (0x5A1995, SPELLS_NUM); _PI->WriteByte (0x5A84C8, SPELLS_NUM); _PI->WriteByte (0x5A852F, SPELLS_NUM); _PI->WriteCodePatch(0x4446D2, "%n", 7); _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_, fillShrine); // Fear, Disease, Age _PI->WriteLoHook(0x444701, applySpell); _PI->WriteLoHook(0x44427E, resetSpell); _PI->WriteHiHook(0x43B2E0, SPLICE_, EXTENDED_, THISCALL_, getSpellWeight);
// Allow to cure new spells _PI->WriteLoHook(0x4462FD, cureNewSpells);
// Death Cloud _PI->WriteLoHook(0x5A0F4C, DeathCloud);
// Summon Firebird, Summon Magic Elemental _PI->WriteLoHook(0x59F889, checkSummonedCreaturesType); _PI->WriteLoHook(0x5A065D, SummonCreatures); _PI->WriteLoHook(0x5A7516, setSummonedCreaturesNumber); // Stack's Magic Vulnerability _PI->WriteLoHook(0x44A268, getStackMagicVulnerability); // Artifacts (incl. Spell Scrolls) _PI->WriteHiHook(0x4D9840, SPLICE_, DIRECT_, THISCALL_, updateSpellsFromArtifacts);
// Can unit attack? Seems to be optional _PI->WriteHiHook(0x4425A0, SPLICE_, DIRECT_, THISCALL_, unitCanAttack); // ------------ // Fear // ------------ // Set cursor for human players _PI->WriteHiHook(0x475DC0, SPLICE_, EXTENDED_, THISCALL_, setCursorForFearSpell); // Not allowing to attack under Fear _PI->WriteLoHook(0x422088, skipMeleeAttackUnderFear); _PI->WriteLoHook(0x41F25C, skipShootingUnderFear);
_PI->WriteLoHook(0x4ADF71, initSpeedMods); int speedModJmpAddr[] = {0x441CE3, 0x441E06, 0x44857E, 0x4485BC, 0x4485FC, 0x44867A, 0x448A05}; for (int i = 0; i < sizeof(speedModJmpAddr) / sizeof(int); ++i) _PI->WriteHexPatch(speedModJmpAddr[i], "90 90"); } }
return TRUE; }
Защита 15 на 2-й и 3-й картинках не баг. Заскринил не в защитной стойке. Хотя я не понял, почему именно 16. Надо смотреть эффекты. * * * UPD: Добавил функции-заглушки взвешивания для всех новых заклинаний. Теперь AI должен кастовать все. Пересмотрел иммунитеты: они теперь такие же, как у оригинальных спецабилок, за исключением того, что Disease можно кастовать на боевые машины (такое решение принято). Добавил функцию лечения новых заклинаний, кроме Age (думаю позже переписать её целиком). Погонял несколько боёв. Вроде, всё работает
|