Атака своего теперь может быть только в том случае, когда это действительно выгодно для отразившего. То есть атака большего кол-ва отрядов вместе со своим будет более выгодной чем атака меньшего кол-ва, но без своего.
Если нам нужно выбрать конкретное кол-во в зависимости от кол-ва отразивших, то в данную функцию потреюбуется передать еще один параметр с кол-вом отразивших, убрать предпоследний цикл с поиском максимального и заменить этим параметром в последнем цикле maxCount.
- Код: Выделить всё
for (int i = 0; i < 187; ++i)
if (countCurSide[i].first > maxCount)
maxCount = countCurSide[i].first;
while (maxHex == -1)
{
for (int i = 0; i < 187; ++i)
if (countCurSide[i].first == maxCount)
{
if (countCurSide[i].second - countEnemySide[i].second > maxValue)
{
maxValue = countCurSide[i].second - countEnemySide[i].second;
maxHex = i;
}
}
if (maxHex == -1 && maxCount > 1) --maxCount; else break;
}
return maxHex;
Также добавил хук для проверки языка и структуру с английским описанием. Чтобы игроки, которые предпочитают играть в оригинале с англ переводом видели англ описание Зеркала, а те, кто с русским - русское. В русской версии Героев и в англ с подключенным плагином руссификации будет русское описание, а в английской версии и в русской с подключенным плагином англификации - английское.
Если Вы знаете более правильное определние языка игры, тогда делитесь.
- Код: Выделить всё
bool isRusLng;
...
int __stdcall isRusLanguage(LoHook* h, HookContext* c)
{
isRusLng = o_CreatureInfo[0].name_single[0] != 'P';
return EXEC_DEFAULT;
}
...
// проверяем язык после загрузки всех txt-файлов игры
_PI->WriteLoHook(0x4EDFFD, isRusLanguage);
- Код: Выделить всё
#define _CRT_SECURE_NO_WARNINGS
#include "stdafx.h"
#include "..\..\HotA\homm3.h"
#include <vector>
#include <algorithm>
using namespace std;
Patcher* _P;
PatcherInstance* _PI;
struct _BattleStackEx_ : public _BattleStack_
{
bool isImmuneTo(const int spell);
bool approvedTarget(const int spell);
bool magicMirrorLucky(const int spell);
_BattleStackEx_* getEnemyStackToRedirectTo(const int spell);
};
bool isRusLng;
bool showMagicMirrorAnim, showMagicResistAnim, showSpellAnim, redirectAreaSpell;
bool needMagicMirrorAnim[2][20], needMagicResistAnim[2][20], needSpellAnim[2][20];
vector<_BattleStackEx_*> stackVector;
int stackIndex;
#define SPL_DEATH_RIPPLE 24
#define CID_AZURE_DRAGON 132
#define AnimateStack (((bool(*)[20])&o_BattleMgr->Field<bool>(0x547C)))
enum A { A_REFLECTSPELL_HOOK, A_MASSREFLECTION_HOOK, A_REFLECTSPELL_SKIP, A_REFLECTSPELL_DEFAULT, A_MASSANIMATION };
enum E { E_MASS, E_BERSERK, E_AREA, E_DEATH_RIPPLE, E_DESTROY_UNDEAD, E_ARMAGEDDON, E_SIZE };
const _ptr_ hAddr[][E_SIZE] =
{
{ 0x5A6A50, 0x5A20F4, 0x5A4D74, 0x5A1006, 0x5A1203, 0x5A4F4E }, // A_REFLECTSPELL_HOOK
{ 0x5A13E5, 0x5A2156, 0x5A4C80, 0x5A1151, 0x5A12BD, 0x5A4FFF }, // A_MASSREFLECTION_HOOK
{ 0x5A6A83, 0x5A210E, 0x5A4D89, 0x5A107B, 0x5A1278, 0x5A4FBB }, // A_REFLECTSPELL_SKIP
{ 0x000000, 0x000000, 0x5A4DA8, 0x5A1020, 0x5A121D, 0x5A4F63 }, // A_REFLECTSPELL_DEFAULT
{ 0x5A13FC, 0x5A2168, 0x000000, 0x5A116F, 0x5A12E0, 0x000000 } // A_MASSANIMATION
};
int __stdcall isRusLanguage(LoHook* h, HookContext* c)
{
isRusLng = o_CreatureInfo[0].name_single[0] != 'P';
return EXEC_DEFAULT;
}
struct SpellDesc
{
char* DefaultDesc;
char* BasicDesc;
char* AdvancedDesc;
char* ExpertDesc;
};
SpellDesc NewMagicMirrorRus =
{
"{Магическое зеркало}\n\nЦелевой отряд способен отразить вражеское заклинание на самый сильный отряд противника с вероятностью в 40%.",
"{Базовое Магическое зеркало}\n\nЦелевой отряд способен отразить вражеское заклинание на самый сильный отряд противника с вероятностью в 40%.",
"{Продвинутое Магическое зеркало}\n\nЦелевой отряд способен отразить вражеское заклинание на самый сильный отряд противника с вероятностью в 50%.",
"{Экспертное Магическое зеркало}\n\nВсе дружественные отряды способны отразить вражеское заклинание на все или самые сильные отряды противника с вероятностью в 50%."
};
SpellDesc NewMagicMirrorEng =
{
"{Magic Mirror}\n\nThe target stack is able to reflect the enemy spell on the strongest enemy stack with a 40% chance.",
"{Basic Magic Mirror}\n\nThe target stack is able to reflect the enemy spell on the strongest enemy stack with a 40% chance.",
"{Advanced Magic Mirror}\n\nThe target stack is able to reflect the enemy spell on the strongest enemy stack with a 50% chance.",
"{Expert Magic Mirror}\n\nAll friendly stacks are able to reflect an enemy spell on all or the most strongest enemy stacks with a 50% chance."
};
int __stdcall afterInit(LoHook* h, HookContext* c)
{
o_Spell[SPL_MAGIC_MIRROR].effect[0] = 40;
o_Spell[SPL_MAGIC_MIRROR].effect[1] = 40;
o_Spell[SPL_MAGIC_MIRROR].effect[2] = 50;
o_Spell[SPL_MAGIC_MIRROR].effect[3] = 50;
if (isRusLng) *(SpellDesc*)o_Spell[SPL_MAGIC_MIRROR].description = NewMagicMirrorRus; else
*(SpellDesc*)o_Spell[SPL_MAGIC_MIRROR].description = NewMagicMirrorEng;
return EXEC_DEFAULT;
}
_ptr_ getRA(const int spell)
{
_ptr_ retAddr;
switch (spell)
{
case SPL_FROST_RING: case SPL_FIREBALL: case SPL_INFERNO: case SPL_METEOR_SHOWER:
retAddr = hAddr[A_REFLECTSPELL_DEFAULT][E_AREA];
break;
case SPL_DEATH_RIPPLE:
retAddr = hAddr[A_REFLECTSPELL_DEFAULT][E_DEATH_RIPPLE];
break;
case SPL_DESTROY_UNDEAD:
retAddr = hAddr[A_REFLECTSPELL_DEFAULT][E_DESTROY_UNDEAD];
break;
case SPL_ARMAGEDDON:
retAddr = hAddr[A_REFLECTSPELL_DEFAULT][E_ARMAGEDDON];
break;
}
return retAddr;
}
bool _BattleStackEx_::isImmuneTo(const int spell)
{
return CALL_7(float, __thiscall, 0x5A83A0, o_BattleMgr, spell, o_BattleMgr->current_side, this, 1, 1, 0) == 0;
}
bool _BattleStackEx_::approvedTarget(const int spell)
{
return !this->is_killed && !this->isImmuneTo(spell) || spell == SPL_FORGETFULNESS && this->creature.shots == 0;
}
bool _BattleStackEx_::magicMirrorLucky(const int spell)
{
int effSchoolLevel = this->active_spells_power[SPL_MAGIC_MIRROR];
return !this->isImmuneTo(spell) && !(o_Spell[spell].flags & SPF_FRIENDLY_HAS_MASS) && (this->side != o_BattleMgr->current_side ||
spell == SPL_FROST_RING || spell == SPL_FIREBALL || spell == SPL_INFERNO || spell == SPL_METEOR_SHOWER ||
spell == SPL_BERSERK || spell == SPL_DEATH_RIPPLE || spell == SPL_DESTROY_UNDEAD || spell == SPL_ARMAGEDDON) &&
this->active_spell_duration[SPL_MAGIC_MIRROR] > 0 && Randint(1, 100) <= o_Spell[SPL_MAGIC_MIRROR].effect[effSchoolLevel];
}
_BattleStackEx_* _BattleStackEx_::getEnemyStackToRedirectTo(const int spell)
{
int sideFoe = 1 - this->side;
_BattleStackEx_* stackFoe = stackIndex < (int)stackVector.size() ? (_BattleStackEx_*)&o_BattleMgr->stack[sideFoe][stackVector[stackIndex]->index_on_side] : 0;
++stackIndex;
return stackFoe;
}
bool __stdcall massSpellMagicMirror(HiHook *h, int spell, int ssLevel)
{
return spell == SPL_MAGIC_MIRROR && ssLevel == 3 ? false : CALL_2(bool, __fastcall, h->GetDefaultFunc(), spell, ssLevel);
}
bool cmpFightValue(_BattleStackEx_* a, _BattleStackEx_* b)
{
return a->creature.fight_value * a->count_current > b->creature.fight_value * b->count_current;
}
// Костыль для C++98
class cmpSpellDuration
{
int spell;
public:
cmpSpellDuration(int id) : spell(id) { }
bool operator () (_BattleStackEx_* a, _BattleStackEx_* b)
{
return a->active_spell_duration[spell] < b->active_spell_duration[spell];
}
};
int __stdcall newTarget(LoHook* h, HookContext* c)
{
c->eax = stackIndex < (int)stackVector.size() ?
(int)&o_BattleMgr->stack[o_BattleMgr->current_side][stackVector[stackIndex]->index_on_side] :
(int)&o_BattleMgr->stack[o_BattleMgr->current_side][0];
return EXEC_DEFAULT;
}
int __stdcall skipSpellSound(LoHook* h, HookContext* c)
{
if (!c->edi) {
int spell = *(int*)(c->ebp + 8);
char switchCase = ((char*)0x5A2B48)[spell - 10];
// При следующих условиях не проигрываем звук заклинания
if (spell == SPL_DEATH_RIPPLE || spell == SPL_DESTROY_UNDEAD ||
switchCase == 17 || switchCase == 24 || o_BattleMgr->ShouldNotRenderBattle())
{
c->return_address = 0x5A0646;
return NO_EXEC_DEFAULT;
}
}
return EXEC_DEFAULT;
}
int __stdcall dataInit(LoHook* h, HookContext* c)
{
int spell = *(int*)(c->ebp + 8);
showMagicMirrorAnim = false;
showMagicResistAnim = false;
showSpellAnim = false;
redirectAreaSpell = false;
memset(needMagicMirrorAnim, false, 40);
memset(needMagicResistAnim, false, 40);
memset(needSpellAnim, false, 40);
int casterSide = o_BattleMgr->current_side;
int maxSize = o_BattleMgr->stacks_count[casterSide];
stackVector.resize(maxSize);
int n = 0;
for (int i = 0; i < maxSize; ++i)
{
_BattleStackEx_* stack = (_BattleStackEx_*)&o_BattleMgr->stack[casterSide][i];
if (stack->approvedTarget(spell)) stackVector[n++] = stack;
}
stackVector.resize(n);
sort(stackVector.begin(), stackVector.end(), cmpFightValue);
stable_sort(stackVector.begin(), stackVector.end(), cmpSpellDuration(spell));
stackIndex = 0;
return EXEC_DEFAULT;
}
int __stdcall magicMirrorAI(LoHook* h, HookContext* c)
{
_BattleStackEx_* stack = (_BattleStackEx_*)c->edi;
if (stack)
{
_Hero_* hero = o_BattleMgr->hero[1 - o_BattleMgr->current_side];
int spell = *(int*)(c->ebp + 8);
int effSchoolLevel = stack->active_spells_power[SPL_MAGIC_MIRROR];
// Если герой - AI, на отряде висит Magic Mirror и оно сработало, а также оригинальные проверки на Teleport и Sacrifice
if (!CALL_2(bool, __thiscall, 0x4CE600, o_GameMgr, o_BattleMgr->owner_id[o_BattleMgr->current_side]) &&
stack->active_spell_duration[SPL_MAGIC_MIRROR] > 0 && Randint(1, 100) <= o_Spell[SPL_MAGIC_MIRROR].effect[effSchoolLevel] &&
spell != SPL_TELEPORT && spell != SPL_SACRIFICE)
{
// Проигрываем одиночную анимацию
CALL_5(void, __thiscall, 0x4963C0, o_BattleMgr, o_Spell[SPL_MAGIC_MIRROR].animation_ix, c->edi, 100, 0);
c->return_address = 0x5A05CC;
return NO_EXEC_DEFAULT;
}
}
return EXEC_DEFAULT;
}
void playResistAndMagicMirror()
{
// Проигрываем массовую анимацию и звук резиста
if (showMagicResistAnim)
{
CALL_3(void, __fastcall, 0x59A890, "MagicRes.wav", -1, 3);
CALL_4(void, __thiscall, 0x5A6AD0, o_BattleMgr, &needMagicResistAnim, 78, 0);
}
// Проигрываем массовую анимацию и звук Magic Mirror
if (showMagicMirrorAnim)
{
CALL_3(void, __fastcall, 0x59A890, o_Spell[SPL_MAGIC_MIRROR].wav_name, -1, 3);
CALL_4(void, __thiscall, 0x5A6AD0, o_BattleMgr, &needMagicMirrorAnim, o_Spell[SPL_MAGIC_MIRROR].animation_ix, 0);
}
}
int __stdcall massReflection(LoHook* h, HookContext* c)
{
bool hitFlag = false;
_ptr_ addr = h->GetAddress();
if (addr == hAddr[A_MASSREFLECTION_HOOK][E_ARMAGEDDON])
{
memcpy(AnimateStack, needSpellAnim, 40);
return EXEC_DEFAULT;
}
if (addr == hAddr[A_MASSREFLECTION_HOOK][E_MASS])
c->return_address = hAddr[A_MASSANIMATION][E_MASS];
if (addr == hAddr[A_MASSREFLECTION_HOOK][E_BERSERK])
c->return_address = hAddr[A_MASSANIMATION][E_BERSERK];
if (addr == hAddr[A_MASSREFLECTION_HOOK][E_DEATH_RIPPLE])
{
hitFlag = true;
c->return_address = hAddr[A_MASSANIMATION][E_DEATH_RIPPLE];
}
if (addr == hAddr[A_MASSREFLECTION_HOOK][E_DESTROY_UNDEAD])
{
c->edi = *(int*)(c->ebp - 0x10);
hitFlag = true;
c->return_address = hAddr[A_MASSANIMATION][E_DESTROY_UNDEAD];
}
playResistAndMagicMirror();
// Проигрываем массовую анимацию и звук кастуемого заклинания
if (showSpellAnim)
{
_Spell_* spell = (_Spell_*)*(int*)(c->ebp - 0x10);
CALL_3(void, __fastcall, 0x59A890, spell->wav_name, -1, 3);
CALL_4(void, __thiscall, 0x5A6AD0, o_BattleMgr, &needSpellAnim, spell->animation_ix, hitFlag);
}
return NO_EXEC_DEFAULT;
}
/*int getHexId(const int spell)
{
int randomStack = Randint(0, o_BattleMgr->stacks_count[o_BattleMgr->current_side] - 1);
return o_BattleMgr->stack[o_BattleMgr->current_side][randomStack].hex_ix;
}*/
int getHexId(int spell)
{
int maxCount = 0;
int maxValue = 0;
int maxHex = -1;
int SumHex;
int dev;
_BattleStackEx_* hex[19];
vector <pair<int, int>> countCurSide;
vector <pair<int, int>> countEnemySide;
countCurSide.resize(187);
countEnemySide.resize(187);
vector <pair<int, _BattleStackEx_*>> wideHexCur;
vector <pair<int, _BattleStackEx_*>> wideHexEnemy;
wideHexCur.resize(187);
wideHexEnemy.resize(187);
int koefCur[187] = { 0 };
int koefEnemy[187] = { 0 };
int test[187] = { 0 };
if (spell == SPL_FROST_RING) SumHex = 6; else
if (spell == SPL_METEOR_SHOWER || spell == SPL_FIREBALL) SumHex = 7; else
if (spell == SPL_INFERNO) SumHex = 19;
for (int i = 0; i < 187; ++i)
{
if (i % 17 == 0 || (i + 1) % 17 == 0) continue;
memset(koefCur, 0, sizeof(koefCur));
memset(koefEnemy, 0, sizeof(koefEnemy));
wideHexCur.clear();
wideHexEnemy.clear();
wideHexCur.resize(187);
wideHexEnemy.resize(187);
if (((i / 17) + 1) % 2 == 0) dev = 0; else dev = 1;
int id[] = { dev - 17, dev - 18, dev + 16, dev + 17, -1, 1, 0, -2, 2,
dev - 16, dev - 19, dev + 15, dev + 18, -33, -34, -35, 33, 34, 35 };
for (int j = 0; j < SumHex; ++j)
hex[j] = (_BattleStackEx_*)o_BattleMgr->hex[i + id[j]].GetCreature();
for (int j = 0; j < SumHex; ++j)
{
if (i + id[j] < 0 || i + id[j] > 186 || (i + id[j]) % 17 == 0 || (i + id[j] + 1) % 17 == 0) continue;
if (hex[j] != 0)
{
if (hex[j]->creature_id >= 0 && hex[j]->creature_id <= 149 && !hex[j]->isImmuneTo(spell))
{
if (hex[j]->side == o_BattleMgr->current_side)
{
if (!(hex[j]->creature.flags & BCF_2HEX_WIDE))
{
++countCurSide[i].first;
countCurSide[i].second += o_pCreatureInfo[hex[j]->creature_id].fight_value * hex[j]->count_current;
}
}
else
{
if (!(hex[j]->creature.flags & BCF_2HEX_WIDE))
{
++countEnemySide[i].first;
countEnemySide[i].second += o_pCreatureInfo[hex[j]->creature_id].fight_value * hex[j]->count_current;
}
}
}
}
}
int min = (i / 17) * 17 - 34;
if (min < 0) min = 0;
int max = (i / 17 + 1) * 17 + 33;
if (max > 186) max = 186;
int dist = min;
int cur = 0;
int enemy = 0;
while (dist <= max)
{
_BattleStackEx_* stack = (_BattleStackEx_*)o_BattleMgr->hex[dist].GetCreature();
if (dist % 17 != 0 && (dist + 1) % 17 != 0 && stack != 0 && stack->creature_id >= 0 && stack->creature_id <= 149 && !stack->isImmuneTo(spell) && (stack->creature.flags & BCF_2HEX_WIDE))
{
if (stack->side == o_BattleMgr->current_side)
{
++cur;
wideHexCur[dist].first = cur;
wideHexCur[dist].second = stack;
if (stack->creature_id != CID_CATAPULT && stack->creature_id != CID_BALLISTA && stack->creature_id != CID_FIRST_AID_TENT)
{
++dist;
wideHexCur[dist].first = cur;
wideHexCur[dist].second = stack;
}
}
else
{
++enemy;
wideHexEnemy[dist].first = enemy;
wideHexEnemy[dist].second = stack;
if (stack->creature_id != CID_CATAPULT && stack->creature_id != CID_BALLISTA && stack->creature_id != CID_FIRST_AID_TENT)
{
++dist;
wideHexEnemy[dist].first = enemy;
wideHexEnemy[dist].second = stack;
}
}
}
++dist;
}
int countCurHexWide = 0;
int sumCurFightValueHexWide = 0;
for (int j = 0; j < SumHex; ++j)
{
if (i + id[j] >= 0 && i + id[j] <= 186 && (i + id[j]) % 17 != 0 && (i + id[j] + 1) % 17 != 0 && wideHexCur[i + id[j]].first != 0)
{
if (koefCur[wideHexCur[i + id[j]].first] == 0)
{
koefCur[wideHexCur[i + id[j]].first] = 1;
++countCurHexWide;
sumCurFightValueHexWide += o_pCreatureInfo[wideHexCur[i + id[j]].second->creature_id].fight_value * wideHexCur[i + id[j]].second->count_current;
}
}
}
int countEnemyHexWide = 0;
int sumEnemyFightValueHexWide = 0;
for (int j = 0; j < SumHex; ++j)
{
if (i + id[j] >= 0 && i + id[j] <= 186 && (i + id[j]) % 17 != 0 && (i + id[j] + 1) % 17 != 0 && wideHexEnemy[i + id[j]].first != 0)
{
if (koefEnemy[wideHexEnemy[i + id[j]].first] == 0)
{
koefEnemy[wideHexEnemy[i + id[j]].first] = 1;
++countEnemyHexWide;
sumEnemyFightValueHexWide += o_pCreatureInfo[wideHexEnemy[i + id[j]].second->creature_id].fight_value * wideHexEnemy[i + id[j]].second->count_current;
}
}
}
countCurSide[i].first += countCurHexWide;
countCurSide[i].second += sumCurFightValueHexWide;
countEnemySide[i].first += countEnemyHexWide;
countEnemySide[i].second += sumEnemyFightValueHexWide;
}
for (int i = 0; i < 187; ++i)
if (countCurSide[i].first > maxCount)
maxCount = countCurSide[i].first;
while (maxHex == -1)
{
for (int i = 0; i < 187; ++i)
if (countCurSide[i].first == maxCount)
{
if (countCurSide[i].second - countEnemySide[i].second > maxValue)
{
maxValue = countCurSide[i].second - countEnemySide[i].second;
maxHex = i;
}
}
if (maxHex == -1 && maxCount > 1) --maxCount; else break;
}
return maxHex;
}
void __stdcall areaReflection(HiHook* h, _BattleMgr_* battleMgr, int hex, int spell, int a3, int a4)
{
memcpy(AnimateStack, needSpellAnim, 40);
CALL_5(void, __thiscall, h->GetDefaultFunc(), battleMgr, hex, spell, a3, a4);
playResistAndMagicMirror();
// Если заклинание отражено
if (redirectAreaSpell)
{
showMagicMirrorAnim = false;
showMagicResistAnim = false;
memset(needMagicMirrorAnim, false, 40);
memset(needMagicResistAnim, false, 40);
CALL_3(void, __fastcall, 0x59A890, o_Spell[spell].wav_name, -1, 3);
if (getHexId(spell) != -1)
CALL_5(void, __thiscall, h->GetDefaultFunc(), battleMgr, getHexId(spell), spell, a3, a4);
playResistAndMagicMirror();
}
}
int __stdcall playMagicMirrorSound(LoHook* h, HookContext* c)
{
CALL_3(void, __fastcall, 0x59A890, o_Spell[SPL_MAGIC_MIRROR].wav_name, -1, 3);
return EXEC_DEFAULT;
}
int __stdcall reflectSpell(LoHook* h, HookContext* c)
{
_Hero_* hero = 0;
_BattleStackEx_* stack = 0;
int spell, spellPower, schoolLevel;
int casterSide = o_BattleMgr->current_side;
_ptr_ addr = h->GetAddress();
if (addr == hAddr[A_REFLECTSPELL_HOOK][E_MASS])
{
stack = (_BattleStackEx_*)c->esi;
spell = *(int*)(c->ebp + 0xC);
spellPower = *(int*)(c->ebp + 0x14);
schoolLevel = *(int*)(c->ebp + 0x10);
hero = *(_Hero_**)(c->ebp + 8);
c->return_address = hAddr[A_REFLECTSPELL_SKIP][E_MASS];
}
if (addr == hAddr[A_REFLECTSPELL_HOOK][E_BERSERK])
{
stack = *(_BattleStackEx_**)(c->ebp + 0x14);
spell = *(int*)(c->ebp + 8);
spellPower = *(int*)(c->ebp + 0x1C);
schoolLevel = c->esi;
hero = *(_Hero_**)(c->ebp - 0x14);
c->return_address = hAddr[A_REFLECTSPELL_SKIP][E_BERSERK];
}
if (addr == hAddr[A_REFLECTSPELL_HOOK][E_AREA])
{
stack = (_BattleStackEx_*)c->edi;
spell = c->esi;
schoolLevel = *(int*)(c->ebp - 0x44);
c->return_address = hAddr[A_REFLECTSPELL_SKIP][E_AREA];
}
if (addr == hAddr[A_REFLECTSPELL_HOOK][E_DEATH_RIPPLE])
{
stack = (_BattleStackEx_*)c->edi;
spell = SPL_DEATH_RIPPLE;
c->return_address = hAddr[A_REFLECTSPELL_SKIP][E_DEATH_RIPPLE];
}
if (addr == hAddr[A_REFLECTSPELL_HOOK][E_DESTROY_UNDEAD])
{
stack = (_BattleStackEx_*)c->edi;
spell = SPL_DESTROY_UNDEAD;
c->return_address = hAddr[A_REFLECTSPELL_SKIP][E_DESTROY_UNDEAD];
}
if (addr == hAddr[A_REFLECTSPELL_HOOK][E_ARMAGEDDON])
{
stack = (_BattleStackEx_*)c->esi;
spell = SPL_ARMAGEDDON;
c->return_address = hAddr[A_REFLECTSPELL_SKIP][E_ARMAGEDDON];
}
// Получаем оригинальный шанс срабатывания заклинания
int hitChance = c->eax;
if (stack)
{
bool isSpellAoE = spell == SPL_FROST_RING || spell == SPL_FIREBALL || spell == SPL_INFERNO || spell == SPL_METEOR_SHOWER;
bool isSpellSpec = spell == SPL_DEATH_RIPPLE || spell == SPL_DESTROY_UNDEAD || spell == SPL_ARMAGEDDON;
if (stack->magicMirrorLucky(spell))
{
showMagicMirrorAnim = true;
needMagicMirrorAnim[stack->side][stack->index_on_side] = true;
// Не перенаправляем площадные заклинания, отражённые от дружественных отрядов
if (!(isSpellAoE && stack->side == casterSide)) redirectAreaSpell = true;
if (isSpellAoE || isSpellSpec) return NO_EXEC_DEFAULT;
// Если сработало Magic Mirror, перенаправляем заклинание на вражеский отряд
_BattleStackEx_* stackFoe = stack->getEnemyStackToRedirectTo(spell);
if (stackFoe)
{
// Определяем, сработает ли резист против отражённого заклинания
int hitChanceFoe = (int)(100 * CALL_7(float, __thiscall, 0x5A83A0, o_BattleMgr, spell,
casterSide, stackFoe, 1, 1, *(int*)(c->ebp + 0x1C)));
if (Randint(1, 100) <= hitChanceFoe)
{
showSpellAnim = true;
needSpellAnim[stackFoe->side][stackFoe->index_on_side] = true;
CALL_5(void, __thiscall, 0x444610, stackFoe, spell, spellPower, schoolLevel, hero);
}
else if (hitChanceFoe)
{
showMagicResistAnim = true;
needMagicResistAnim[stackFoe->side][stackFoe->index_on_side] = true;
}
}
}
else
{
if (Randint(1, 100) <= hitChance)
{
showSpellAnim = true;
needSpellAnim[stack->side][stack->index_on_side] = true;
if (isSpellAoE || isSpellSpec)
{
c->return_address = getRA(spell);
return NO_EXEC_DEFAULT;
}
// Если не сработал резист, применяем эффект заклинания
CALL_5(void, __thiscall, 0x444610, stack, spell, spellPower, schoolLevel, hero);
}
else if (hitChance)
{
showMagicResistAnim = true;
needMagicResistAnim[stack->side][stack->index_on_side] = true;
if (isSpellAoE || isSpellSpec) return NO_EXEC_DEFAULT;
}
}
}
return NO_EXEC_DEFAULT;
}
int __stdcall afterArmageddon(LoHook* h, HookContext* c)
{
playResistAndMagicMirror();
return EXEC_DEFAULT;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
static bool plugin_On = false;
if (DLL_PROCESS_ATTACH == ul_reason_for_call)
{
if (!plugin_On)
{
plugin_On = true;
_P = GetPatcher();
_PI = _P->CreateInstance((char*)"HD.Plugin.NewMagicMirrorTest");
// проверяем язык после загрузки всех txt-файлов игры
_PI->WriteLoHook(0x4EDFFD, isRusLanguage);
// Пропускаем оригинальный звук заклинания, чтобы воспроизвезти его позже
_PI->WriteLoHook(0x5A0634, skipSpellSound);
// Инициализируем данные
_PI->WriteLoHook(0x5A058B, dataInit);
// Синглкаст
_PI->WriteLoHook(0x5A05CC, newTarget);
_PI->WriteLoHook(0x5A058B, magicMirrorAI);
// Масскаст
for (int i = 0; i < E_SIZE; ++i)
{
_PI->WriteLoHook(hAddr[A_REFLECTSPELL_HOOK][i], reflectSpell);
if (i == E_AREA)
_PI->WriteHiHook(hAddr[A_MASSREFLECTION_HOOK][i], SPLICE_, EXTENDED_, THISCALL_, areaReflection);
else
_PI->WriteLoHook(hAddr[A_MASSREFLECTION_HOOK][i], massReflection);
}
// Проигрываем звук Magic Mirror при отражении одиночного заклинания
_PI->WriteLoHook(0x5A059A, playMagicMirrorSound);
_PI->WriteLoHook(0x4EE1C1, afterInit);
_PI->WriteHiHook(0x59E360, SPLICE_, EXTENDED_, FASTCALL_, massSpellMagicMirror);
// Проигрываем анимацию резиста и Magic Mirror для Armageddon
_PI->WriteLoHook(0x5A5560, afterArmageddon);
}
}
return TRUE;
}