- Код: Выделить всё
#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 approvedTarget(const int spell);
bool magicMirrorLucky(const int spell);
_BattleStackEx_* getStackToRedirect(const int spell);
inline int fightValue() {
return this->creature.fight_value * this->count_current;
};
inline float getMagicVulnerability(const int spell) {
return CALL_7(float, __thiscall, 0x5A83A0, o_BattleMgr, spell, o_BattleMgr->current_side, this, 1, 1, 0);
};
inline int getHitChance(const int spell) {
return (int)(100 * getMagicVulnerability(spell));
}
inline bool isImmuneTo(const int spell) {
return getMagicVulnerability(spell) == 0;
};
};
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
};
struct SpellDesc
{
char* DefaultDesc;
char* BasicDesc;
char* AdvancedDesc;
char* ExpertDesc;
};
SpellDesc NewMagicMirror =
{
"{Магическое зеркало}\n\nЦелевой отряд способен отразить вражеское заклинание на самый сильный отряд противника с вероятностью в 40%.",
"{Базовое Магическое зеркало}\n\nЦелевой отряд способен отразить вражеское заклинание на самый сильный отряд противника с вероятностью в 40%.",
"{Продвинутое Магическое зеркало}\n\nЦелевой отряд способен отразить вражеское заклинание на самый сильный отряд противника с вероятностью в 50%.",
"{Экспертное Магическое зеркало}\n\nВсе дружественные отряды способны отразить вражеское заклинание на все или самые сильные отряды противника с вероятностью в 50%."
};
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;
*(SpellDesc*)o_Spell[SPL_MAGIC_MIRROR].description = NewMagicMirror;
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_::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_::getStackToRedirect(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)
{
if ( stackIndex < (int)stackVector.size() )
c->eax = (int)&o_BattleMgr->stack[o_BattleMgr->current_side][stackVector[stackIndex]->index_on_side];
else {
int i = 0;
// Пропускаем погибшие отряды
while ( o_BattleMgr->stack[o_BattleMgr->current_side][i].is_killed ) ++i;
c->eax = (int)&o_BattleMgr->stack[o_BattleMgr->current_side][i];
}
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 getMonsterSpell(const int creature_id)
{
int spell;
switch ( creature_id )
{
case CID_THUNDERBIRD:
spell = SPL_LIGHTNING_BOLT;
break;
case CID_DRAGON_FLY:
spell = SPL_WEAKNESS;
break;
case CID_UNICORN: case CID_WAR_UNICORN:
spell = SPL_BLIND;
break;
case CID_BLACK_KNIGHT: case CID_DREAD_KNIGHT: case CID_MUMMY:
spell = SPL_CURSE;
break;
}
return spell;
}
int __stdcall dataInit(LoHook* h, HookContext* c)
{
int spell = h->GetAddress() == 0x5A058B ? *(int*)(c->ebp + 8) :
getMonsterSpell(((_BattleStackEx_*)c->ecx)->creature_id);
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 playMagicResist()
{
// Проигрываем массовую анимацию и звук резиста
if ( showMagicResistAnim )
{
CALL_3(void, __fastcall, 0x59A890, "MagicRes.wav", -1, 3);
CALL_4(void, __thiscall, 0x5A6AD0, o_BattleMgr, &needMagicResistAnim, 78, 0);
}
}
void playMagicMirror()
{
// Проигрываем массовую анимацию и звук 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);
}
}
void playResistAndMagicMirror()
{
playMagicResist();
playMagicMirror();
}
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;
}
bool isValidHex(const int hex)
{
return hex >= 0 && hex < 187;
}
int getSpellTargetHex(const int spell)
{
const int xSize = 17;
int maxFightValueDelta = 0;
int bestTargetHex = -1;
for (int hex = 1; ; ++hex)
{
if ( hex % xSize == 16 ) hex += 2;
if ( hex > 185 ) break;
int y = hex / xSize;
int d = y % 2;
int areaR1[] = {-17 - d, -16 - d, -1, 0, 1, 17 - d, 18 - d};
int areaFR[] = {-17 - d, -16 - d, -1, 1, 17 - d, 18 - d};
int areaR2[] = {-35, -34, -33, -18 - d, -17 - d, -16 - d, -15 - d, -2, -1,
0, 1, 2, 16 - d, 17 - d, 18 - d, 19 - d, 33, 34, 35};
int* area = areaR1;
int areaSize = sizeof(areaR1) / sizeof(int);
if ( spell == SPL_FROST_RING ) {
area = areaFR;
areaSize = sizeof(areaFR) / sizeof(int);
}
if ( spell == SPL_INFERNO ) {
area = areaR2;
areaSize = sizeof(areaR2) / sizeof(int);
}
int countUs = 0, countThem = 0, fightValueUs = 0, fightValueThem = 0;
for (int i = 0; i < areaSize; ++i)
{
int iHex = hex + area[i];
if ( isValidHex(iHex) )
{
_BattleStackEx_* stack = (_BattleStackEx_*)o_BattleMgr->hex[iHex].GetCreature();
if ( stack )
{
if ( !stack->is_killed && !stack->isImmuneTo(spell) )
{
if ( stack->side == o_BattleMgr->current_side ) {
++countThem;
fightValueThem += stack->fightValue();
}
else {
++countUs;
fightValueUs += stack->fightValue();
}
}
if ( i + 1 < areaSize )
{
int iHexNext = hex + area[i + 1];
if ( isValidHex(iHexNext) && (_BattleStackEx_*)o_BattleMgr->hex[iHexNext].GetCreature() == stack ) ++i;
}
}
}
}
if ( countUs + countThem > 0 && fightValueThem - fightValueUs > maxFightValueDelta )
{
maxFightValueDelta = fightValueThem - fightValueUs;
bestTargetHex = hex;
}
}
return bestTargetHex;
}
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 && getSpellTargetHex(spell) != -1 )
{
showMagicMirrorAnim = false;
showMagicResistAnim = false;
memset(needMagicMirrorAnim, false, 40);
memset(needMagicResistAnim, false, 40);
CALL_3(void, __fastcall, 0x59A890, o_Spell[spell].wav_name, -1, 3);
CALL_5(void, __thiscall, h->GetDefaultFunc(), battleMgr, getSpellTargetHex(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->getStackToRedirect(spell);
if ( stackFoe )
{
// Определяем, сработает ли резист против отражённого заклинания
int hitChanceFoe = stackFoe->getHitChance(spell);
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;
}
int __stdcall mirrorMonsterSpell(LoHook* h, HookContext* c)
{
int spell;
_BattleStackEx_* stack = (_BattleStackEx_*)c->edi;
_ptr_ addrHit, addrResist;
switch ( h->GetAddress() )
{
case 0x440EE8:
spell = SPL_LIGHTNING_BOLT;
addrHit = 0x440F03;
addrResist = 0x4412AB;
break;
case 0x441178:
spell = SPL_WEAKNESS;
addrHit = 0x441193;
addrResist = 0x4412AB;
break;
case 0x440372:
spell = SPL_BLIND;
addrHit = 0x44038D;
addrResist = 0x4402AE;
break;
case 0x4404DB:
spell = SPL_CURSE;
addrHit = 0x4404F6;
addrResist = 0x4402AE;
break;
}
if ( stack->magicMirrorLucky(spell) )
{
showMagicMirrorAnim = true;
needMagicMirrorAnim[stack->side][stack->index_on_side] = true;
playMagicMirror();
// Если сработало Magic Mirror, перенаправляем заклинание на вражеский отряд
_BattleStackEx_* stackFoe = stack->getStackToRedirect(spell);
if ( stackFoe )
{
// Определяем, сработает ли резист против отражённого заклинания
int hitChanceFoe = stackFoe->getHitChance(spell);
if ( Randint(1, 100) <= hitChanceFoe )
{
c->edi = (int)stackFoe;
c->return_address = addrHit;
return NO_EXEC_DEFAULT;
}
else if ( hitChanceFoe )
{
showMagicResistAnim = true;
needMagicResistAnim[stackFoe->side][stackFoe->index_on_side] = true;
playMagicResist();
c->return_address = addrResist;
return NO_EXEC_DEFAULT;
}
}
}
else
{
int hitChance = stack->getHitChance(spell);
if ( Randint(1, 100) <= hitChance )
{
c->return_address = addrHit;
return NO_EXEC_DEFAULT;
}
else if ( hitChance )
{
showMagicResistAnim = true;
needMagicResistAnim[stack->side][stack->index_on_side] = true;
playMagicResist();
c->return_address = addrResist;
return NO_EXEC_DEFAULT;
}
}
return EXEC_DEFAULT;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
static bool plugin_On = false;
if ( DLL_PROCESS_ATTACH == ul_reason_for_call )
{
if ( !plugin_On )
{
plugin_On = true;
_P = GetPatcher();
_PI = _P->CreateInstance((char*)"HD.Plugin.NewMagicMirrorTest");
// Пропускаем оригинальный звук заклинания, чтобы воспроизвезти его позже
_PI->WriteLoHook(0x5A0634, skipSpellSound);
// Инициализируем данные
_PI->WriteLoHook(0x5A058B, dataInit);
_PI->WriteLoHook(0x4408E0, dataInit);
_PI->WriteLoHook(0x440220, 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);
_PI->WriteLoHook(0x440EE8, mirrorMonsterSpell); // Thunderstrike
_PI->WriteLoHook(0x441178, mirrorMonsterSpell); // Weakness
_PI->WriteLoHook(0x440372, mirrorMonsterSpell); // Blind
_PI->WriteLoHook(0x4404DB, mirrorMonsterSpell); // Curse
}
}
return TRUE;
}
Остались тесты (+ что там с Гипнозом?, + замена, где это нужно, LoadWAVplayAsync() на PlayWAVFile()). Плюс нужно объединить newTarget() и getStackToRedirect() и доработать, потому как вторая функция возвращает 0 в случае отсутствия отрядов в векторе, что корректно обрабатывается только при масскасте.