-
AlexSpl
имя: Александр
- Эксперт
-
- Сообщения: 5587
- Зарегистрирован: 17 сен 2010, 12:58
- Пол:
- Награды: 14
-
-
- Поблагодарили: 2185 раз.
|
AlexSpl » 12 апр 2021, 15:25
Код на текущий момент без пунктов 1) и 2): - Код: Выделить всё
#define _CRT_RAND_S #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_ { int getMonsterSpell(); bool approvedTarget(const int spell); bool magicMirrorLucky(const int spell); _BattleStackEx_* getStackToRedirect(const int spell, const bool massCast = true); int calcMagicDamage(const int spell, const _Hero_* attHero, const _Hero_* defHero); float getFullVulnerability(const int spell); inline int current_health() { return this->count_current * this->full_hp - this->lost_hp; } inline int full_health() { return this->count_current * this->full_hp; } inline int fightValue() { return this->creature.fight_value * this->count_current; }; inline double damageFightValue(const int spell, const int spellDamage) { return this->getFullVulnerability(spell) * min((double)spellDamage / this->full_hp, this->count_current - (double)this->lost_hp / this->full_hp) * this->creature.fight_value; } inline double extraFightValue(const int spell, const int spellDamage) { return spellDamage >= this->current_health() && spellDamage < this->full_health() ? this->getFullVulnerability(spell) * this->creature.fight_value : 0; } 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 getHitChanceResist(const int spell) { return (int)(100 * getMagicVulnerability(spell)); } inline int getHitChanceFull(const int spell) { return (int)(100 * getFullVulnerability(spell)); } inline bool isImmuneTo(const int spell) { return getMagicVulnerability(spell) == 0; }; inline void playAnimation(const int id) { CALL_5(void, __thiscall, 0x4963C0, o_BattleMgr, id, this, 100, 0); } };
bool showMagicMirrorAnim, showMagicResistAnim, showSpellAnim, redirectedAreaSpell; bool showResistAfterAttack = false, showMagicMirrorAfterAttack = false; bool needMagicMirrorAnim[2][20], needMagicResistAnim[2][20], needSpellAnim[2][20]; _BattleStackEx_* targetStack = 0;
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 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;
*(SpellDesc*)o_Spell[SPL_MAGIC_MIRROR].description = o_CreatureInfo[0].name_single[0] != 'P' ? NewMagicMirrorRus : NewMagicMirrorEng;
return EXEC_DEFAULT; }
int Randint_s() { unsigned dice; rand_s(&dice); return (int)((double)dice / ((double)UINT_MAX + 1) * 100.0) + 1; }
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); }
_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_s() <= o_Spell[SPL_MAGIC_MIRROR].effect[effSchoolLevel]; }
_BattleStackEx_* _BattleStackEx_::getStackToRedirect(const int spell, const bool massCast) { _BattleStackEx_* stackFoe = 0; int sideFoe = 1 - this->side;
if ( stackIndex < (int)stackVector.size() && spell != SPL_DESTROY_UNDEAD && spell != SPL_DEATH_RIPPLE && spell != SPL_ARMAGEDDON ) stackFoe = (_BattleStackEx_*)&o_BattleMgr->stack[sideFoe][stackVector[stackIndex++]->index_on_side];
if ( !massCast && !stackFoe ) { int i = 0; // Пропускаем погибшие отряды while ( o_BattleMgr->stack[o_BattleMgr->current_side][i].is_killed ) ++i; stackFoe = (_BattleStackEx_*)&o_BattleMgr->stack[sideFoe][i]; } return stackFoe; }
float _BattleStackEx_::getFullVulnerability(const int spell) { float magicMirrorChance = this->active_spell_duration[SPL_MAGIC_MIRROR] ? o_Spell[SPL_MAGIC_MIRROR].effect[this->active_spells_power[SPL_MAGIC_MIRROR]] * 0.01f : 0; return (1 - magicMirrorChance) * this->getMagicVulnerability(spell); };
int _BattleStackEx_::calcMagicDamage(const int spell, const _Hero_* attHero, const _Hero_* defHero) { int effSchoolLevel = 0; if ( attHero ) effSchoolLevel = CALL_3(int, __thiscall, 0x4E52F0, attHero, spell, o_BattleMgr->spec_terr_type); // Если кастер - нейтралы на Magic Plains else if ( o_BattleMgr->spec_terr_type == 1 ) effSchoolLevel = 3;
int damage = attHero->power * o_Spell[spell].eff_power + o_Spell[spell].effect[effSchoolLevel]; return CALL_7(int, __thiscall, 0x5A7BF0, o_BattleMgr, damage, spell, attHero, defHero, this, 0); }
int __stdcall newTarget(LoHook* h, HookContext* c) { _BattleStackEx_* stack = (_BattleStackEx_*)c->edi; int spell = *(int*)(c->ebp + 8);
c->eax = (int)stack->getStackToRedirect(spell, false);
return EXEC_DEFAULT; }
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); }
// Костыль для C++98 class cmpFightValue { int spell;
public: cmpFightValue(int id) : spell(id) { }
bool operator () (_BattleStackEx_* a, _BattleStackEx_* b) { return a->getFullVulnerability(spell) * a->fightValue() > b->getFullVulnerability(spell) * b->fightValue(); } };
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 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 _BattleStackEx_::getMonsterSpell() { int spell = ID_NONE;
if ( this ) { switch ( this->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; if ( h->GetAddress() == 0x5A058B ) spell = *(int*)(c->ebp + 8); else { _BattleStackEx_* stack = (_BattleStackEx_*)c->esi; spell = stack->getMonsterSpell(); }
if ( spell != ID_NONE ) { showMagicMirrorAnim = false; showMagicResistAnim = false; showSpellAnim = false; redirectedAreaSpell = 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(spell)); 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_s() <= o_Spell[SPL_MAGIC_MIRROR].effect[effSchoolLevel] && spell != SPL_TELEPORT && spell != SPL_SACRIFICE ) { // Проигрываем одиночную анимацию stack->playAnimation(o_Spell[SPL_MAGIC_MIRROR].animation_ix);
c->return_address = 0x5A05CC; return NO_EXEC_DEFAULT; } } return EXEC_DEFAULT; }
void playSound(const char* fileName) { CALL_1(_Sample_, __fastcall, 0x59A770, fileName); }
void playMagicResist() { // Проигрываем массовую анимацию и звук резиста if ( showMagicResistAnim ) { playSound("MagicRes.wav"); CALL_4(void, __thiscall, 0x5A6AD0, o_BattleMgr, &needMagicResistAnim, 78, 0); } }
void playMagicMirror() { // Проигрываем массовую анимацию и звук Magic Mirror if ( showMagicMirrorAnim ) { playSound(o_Spell[SPL_MAGIC_MIRROR].wav_name); 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_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]; }
playMagicMirror(); playMagicResist(); if ( addr == hAddr[A_MASSREFLECTION_HOOK][E_ARMAGEDDON] ) { memcpy(AnimateStack, needSpellAnim, 40); return EXEC_DEFAULT; }
// Проигрываем массовую анимацию и звук кастуемого заклинания if ( showSpellAnim ) { _Spell_* spell = (_Spell_*)*(int*)(c->ebp - 0x10); playSound(spell->wav_name); 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; vector <pair<double, double>> ones; ones.resize(186);
double maxDamageFightValueDelta = 0.0; int bestTargetHex = -1;
_Hero_* attHero = o_BattleMgr->hero[o_BattleMgr->current_side]; _Hero_* defHero = o_BattleMgr->hero[1 - o_BattleMgr->current_side];
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; double extraFightValueUs = 0, extraFightValueThem = 0; double damageFightValueUs = 0, damageFightValueThem = 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) ) { // Считаем магический урон, получаемый стеком stack int spellDamage = stack->calcMagicDamage(spell, attHero, defHero); if ( stack->side == o_BattleMgr->current_side ) { ++countThem; damageFightValueThem += stack->damageFightValue(spell, spellDamage); extraFightValueThem += stack->extraFightValue(spell, spellDamage); } else { ++countUs; damageFightValueUs += stack->damageFightValue(spell, spellDamage); extraFightValueUs += stack->extraFightValue(spell, spellDamage); } } if ( i + 1 < areaSize ) { int iHexNext = hex + area[i + 1]; if ( isValidHex(iHexNext) && (_BattleStackEx_*)o_BattleMgr->hex[iHexNext].GetCreature() == stack ) ++i; } } } }
ones[hex].first = damageFightValueThem - damageFightValueUs; ones[hex].second = extraFightValueThem - extraFightValueUs;
if ( countThem + countUs > 0 && damageFightValueThem - damageFightValueUs > maxDamageFightValueDelta ) { maxDamageFightValueDelta = damageFightValueThem - damageFightValueUs; bestTargetHex = hex; } }
for (int i = 1; i < 186; ++i) if ( maxDamageFightValueDelta > 0.0 && ones[i].first == maxDamageFightValueDelta ) { if ( ones[i].second > ones[bestTargetHex].second ) bestTargetHex = i; }
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); playMagicResist(); playMagicMirror();
// Если заклинание отражено if ( redirectedAreaSpell ) { int bestHex = getSpellTargetHex(spell); if ( bestHex != ID_NONE ) { showMagicMirrorAnim = false; showMagicResistAnim = false; memset(needMagicMirrorAnim, false, 40); memset(needMagicResistAnim, false, 40);
playSound(o_Spell[spell].wav_name); CALL_5(void, __thiscall, h->GetDefaultFunc(), battleMgr, bestHex, spell, a3, a4); playMagicResist(); playMagicMirror(); } } }
int __stdcall playMagicMirrorSound(LoHook* h, HookContext* c) { playSound(o_Spell[SPL_MAGIC_MIRROR].wav_name); 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) ) { _BattleStackEx_* stackFoe = stack->getStackToRedirect(spell);
if ( stackFoe && !redirectedAreaSpell ) { showMagicMirrorAnim = true; needMagicMirrorAnim[stack->side][stack->index_on_side] = true; } else { showMagicResistAnim = true; needMagicResistAnim[stack->side][stack->index_on_side] = true; }
// Не перенаправляем площадные заклинания, отражённые от дружественных отрядов if ( isSpellAoE && stack->side != casterSide ) redirectedAreaSpell = true; if ( isSpellAoE || isSpellSpec ) return NO_EXEC_DEFAULT;
// Если сработало Magic Mirror, перенаправляем заклинание на вражеский отряд if ( stackFoe ) { // Определяем, сработает ли резист против отражённого заклинания int hitChanceFoe = stackFoe->getHitChanceFull(spell);
if ( Randint_s() <= 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_s() <= 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; } } } return NO_EXEC_DEFAULT; }
int __stdcall mirrorMonsterSpell(LoHook* h, HookContext* c) { int spell; _BattleStackEx_* stack = (_BattleStackEx_*)c->edi;
_ptr_ addrHit, addrSkip; switch ( h->GetAddress() ) { case 0x440EE8: spell = SPL_LIGHTNING_BOLT; addrHit = 0x440F03; addrSkip = 0x4412AB; break; case 0x440372: spell = SPL_BLIND; addrHit = 0x44038D; addrSkip = 0x4402AE; break; case 0x440467: spell = SPL_CURSE; addrHit = 0x440482; addrSkip = 0x4402AE; break; }
if ( stack->magicMirrorLucky(spell) ) { c->return_address = addrSkip; // Если сработало Magic Mirror, отражаем обратно // _BattleStackEx_* stackFoe = (_BattleStackEx_*)c->esi; _BattleStackEx_* stackFoe = stack->getStackToRedirect(spell, false); if ( stackFoe ) { int hitChanceFoe = stackFoe->getHitChanceResist(spell); if ( Randint_s() <= hitChanceFoe ) { if ( spell == SPL_LIGHTNING_BOLT ) { playSound(o_Spell[SPL_MAGIC_MIRROR].wav_name); stack->playAnimation(o_Spell[SPL_MAGIC_MIRROR].animation_ix); } else { targetStack = stack; showMagicMirrorAfterAttack = true; }
c->edi = (int)stackFoe; c->return_address = addrHit; } else if ( hitChanceFoe ) { if ( spell == SPL_LIGHTNING_BOLT ) { playSound("MagicRes.wav"); stackFoe->playAnimation(78); } else { targetStack = stackFoe; showResistAfterAttack = true; } } } return NO_EXEC_DEFAULT; } return EXEC_DEFAULT; }
int __stdcall playAnimForMonster(LoHook* h, HookContext* c) { if ( showResistAfterAttack ) { playSound("MagicRes.wav"); targetStack->playAnimation(78); showResistAfterAttack = false; }
if ( showMagicMirrorAfterAttack ) { playSound(o_Spell[SPL_MAGIC_MIRROR].wav_name); targetStack->playAnimation(o_Spell[SPL_MAGIC_MIRROR].animation_ix); showMagicMirrorAfterAttack = false; }
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.NewMagicMirrorBeta");
// Пропускаем оригинальный звук заклинания, чтобы воспроизвезти его позже _PI->WriteLoHook(0x5A0634, skipSpellSound); // Инициализируем данные _PI->WriteLoHook(0x441893, dataInit); _PI->WriteLoHook(0x441991, dataInit); _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);
_PI->WriteLoHook(0x440EE8, mirrorMonsterSpell); // Thunderstrike _PI->WriteLoHook(0x440372, mirrorMonsterSpell); // Blind _PI->WriteLoHook(0x440467, mirrorMonsterSpell); // Curse _PI->WriteLoHook(0x468C99, playAnimForMonster); } }
return TRUE; }
Для абилок сделал отражение, как и для всей остальной магии, чтобы можно было потестить отражение Curse. Если всё-таки нужно, чтобы отражало только на существо с абилкой, закомментируйте третью строчку и раскомментируйте вторую: - Код: Выделить всё
// Если сработало Magic Mirror, отражаем обратно // _BattleStackEx_* stackFoe = (_BattleStackEx_*)c->esi; _BattleStackEx_* stackFoe = stack->getStackToRedirect(spell, false);
Но тогда реально отражение будет работать для Thunderbirds и единорогов, т.к. Curse не действует на нежить. * * * Для масскаста, в случае если заклинание отражается на отряд под Magic Mirror, учёл шанс отражения. Но есть проблема синглкаста. В оригинале отражённое заклинание не может быть перенаправлено, даже если на отряде висит Magic Mirror, а посему Magic Mirror не увеличивает шанс резиста, т.е. как раз отражённые заклинания будут проходить всегда без наличия резиста в случае синглкаста (так, как у нас было до этого в случае масскаста). Получается, придётся править также и синглкаст, чтобы привести всё к общему знаменателю, либо убирать резист от зеркала для отражённых заклинаний в других случаях.
|