-
AlexSpl
имя: Александр
- Эксперт
-
- Сообщения: 5587
- Зарегистрирован: 17 сен 2010, 12:58
- Пол:
- Награды: 14
-
-
- Поблагодарили: 2185 раз.
|
AlexSpl » 16 апр 2021, 23:13
Авторы: AlexSpl, Rolex Название: NewMagicMirror Описание: Существенно усиливает заклинание Magic Mirror. На базовом уровне Магии воздуха или без неё целевой отряд способен отразить вражеское заклинание на самый сильный отряд противника с вероятностью 40%, на продвинутом - с вероятностью 50%, на экспертном уровне заклинание становится массовым: все дружественные отряды способны отразить вражеское заклинание на все или самые сильные отряды противника с вероятностью 50%. Отражение работает, в том числе, и для заклинаний, наносящих урон по площади, а также для магии, накладываемой при атаке следующими существами: Thunderbird, Unicorn, War Unicorn, Black Knight, Dread Knight, Mummy. Плагин совместим с плагином NewSpells. Код - Код: Выделить всё
#define _CRT_SECURE_NO_WARNINGS #include "stdafx.h" #include "..\..\HotA\homm3.h" #include <vector> #include <algorithm>
#define SPL_DEATH_CLOUD_NEW 82 #define SPL_INCINERATION 92
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->full_hp >= this->full_hp - this->lost_hp && spellDamage < this->current_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); } };
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 cmpDamageFightValue { int spell; _Hero_* attHero; _Hero_* defHero;
public: cmpDamageFightValue(int id, _Hero_* aHero, _Hero_* dHero) : spell(id), attHero(aHero), defHero(dHero) { }
bool operator () (_BattleStackEx_* a, _BattleStackEx_* b) { return a->damageFightValue(spell, a->calcMagicDamage(spell, attHero, defHero)) > b->damageFightValue(spell, b->calcMagicDamage(spell, attHero, defHero)); } };
class cmpExtraFightValue { int spell; _Hero_* attHero; _Hero_* defHero;
public: cmpExtraFightValue(int id, _Hero_* aHero, _Hero_* dHero) : spell(id), attHero(aHero), defHero(dHero) { }
bool operator () (_BattleStackEx_* a, _BattleStackEx_* b) { return a->extraFightValue(spell, a->calcMagicDamage(spell, attHero, defHero)) > b->extraFightValue(spell, b->calcMagicDamage(spell, attHero, defHero)); } };
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]; } };
bool showMagicMirrorAnim, showMagicResistAnim, showSpellAnim; bool redirectedSingleCast, 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; }
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: case SPL_DEATH_CLOUD_NEW: case SPL_INCINERATION: 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); }
int getEffSchoolLevel(const _Hero_* hero, const int spell) { return CALL_3(int, __thiscall, 0x4E52F0, hero, spell, o_BattleMgr->spec_terr_type); }
bool _BattleStackEx_::magicMirrorLucky(const int spell) { int effSchoolLevel = getEffSchoolLevel(o_BattleMgr->hero[this->side], 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_DEATH_CLOUD_NEW || spell == SPL_INCINERATION || 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, 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 = getEffSchoolLevel(attHero, SPL_MAGIC_MIRROR); // Если кастер - нейтралы на 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); redirectedSingleCast = true;
return EXEC_DEFAULT; }
float __stdcall calcMagicMirrorResist(HiHook* h, _BattleMgr_* battleMgr, int spell, int side, _BattleStackEx_* stack, int a5, int a6, int a7) { float magicVulnerability = CALL_7(float, __thiscall, h->GetDefaultFunc(), o_BattleMgr, spell, side, stack, a5, a6, a7); float magicMirrorChance = stack->active_spell_duration[SPL_MAGIC_MIRROR] ? o_Spell[SPL_MAGIC_MIRROR].effect[stack->active_spells_power[SPL_MAGIC_MIRROR]] * 0.01f : 0; return redirectedSingleCast ? (1 - magicMirrorChance) * magicVulnerability : magicVulnerability; }
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); }
int _BattleStackEx_::getMonsterSpell() { int spell = ID_NONE;
if ( this ) { switch ( this->creature_id ) { case CID_THUNDERBIRD: spell = SPL_THUNDERBOLT; 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; vector<_BattleStackEx_*> tmpVector; 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; redirectedSingleCast = 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); tmpVector.resize(maxSize);
int n = 0; int k = 0; for (int i = 0; i < maxSize; ++i) { _BattleStackEx_* stack = (_BattleStackEx_*)&o_BattleMgr->stack[casterSide][i]; if ( stack->approvedTarget(spell) ) { if ( stack->active_spell_duration[SPL_BLIND] || stack->active_spell_duration[SPL_STONE] || stack->active_spell_duration[SPL_PARALYZE] ) { // В этот вектор помещаем отряды под Blind/Stone/Paralyze, tmpVector[k++] = stack; } else { // а в этот - все остальные. stackVector[n++] = stack; } } }
stackVector.resize(n); tmpVector.resize(k); // Ударные заклинания (можете также попробовать вместо условия ниже следующее: o_Spell[spell].flags & 0x200) if ( spell >= SPL_MAGIC_ARROW && spell <= SPL_CHAIN_LIGHTNING || spell == SPL_TITANS_LIGHTNING_BOLT ) { _Hero_* attHero = o_BattleMgr->hero[o_BattleMgr->current_side]; _Hero_* defHero = o_BattleMgr->hero[1 - o_BattleMgr->current_side];
sort(stackVector.begin(), stackVector.end(), cmpExtraFightValue(spell, attHero, defHero)); stable_sort(stackVector.begin(), stackVector.end(), cmpDamageFightValue(spell, attHero, defHero)); } // Неударные заклинания else { // Сортируем отряды не под Blind/Stone/Paralyze sort(stackVector.begin(), stackVector.end(), cmpFightValue(spell)); stable_sort(stackVector.begin(), stackVector.end(), cmpSpellDuration(spell)); // Сортируем отряды под Blind/Stone/Paralyze sort(tmpVector.begin(), tmpVector.end(), cmpFightValue(spell)); stable_sort(tmpVector.begin(), tmpVector.end(), cmpSpellDuration(spell));
// Объединяем отсортированные векторы stackVector.insert(stackVector.end(), tmpVector.begin(), tmpVector.end()); }
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 = getEffSchoolLevel(hero, 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 ) { // Проигрываем одиночную анимацию 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) { if (!o_BattleMgr->ShouldNotRenderBattle()) 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 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 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) ) { // Переходим к следующей области, если хотя бы один отряд в ней находится под действием заклинаний: if ( stack->active_spell_duration[SPL_BLIND] || stack->active_spell_duration[SPL_STONE] || stack->active_spell_duration[SPL_PARALYZE] ) { ones[hex].first = 0; goto skipArea; } // Считаем магический урон, получаемый стеком 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; }
skipArea:; }
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 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 || spell == SPL_DEATH_CLOUD_NEW || spell == SPL_INCINERATION; 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(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; AnimateStack[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_THUNDERBOLT; 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; if ( stackFoe ) { int hitChanceFoe = stackFoe->getHitChanceResist(spell); if ( Randint(1, 100) <= hitChanceFoe ) { if ( spell == SPL_THUNDERBOLT ) { 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_THUNDERBOLT ) { 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; }
int __stdcall playMagicMirrorSound(LoHook* h, HookContext* c) { playSound(o_Spell[SPL_MAGIC_MIRROR].wav_name); 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); _PI->WriteHiHook(0x5A83A0, SPLICE_, EXTENDED_, THISCALL_, calcMagicMirrorResist);
// Масскаст 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; }
Исправлено отсутствие анимации для Expert Bloodlust. Исправлено воспроизведение звуков заклинаний во время быстрого боя. Спасибо Нмеса за баг-репорты.
Последний раз редактировалось AlexSpl 29 апр 2022, 16:13, всего редактировалось 7 раз(а).
|