-
AlexSpl
имя: Александр
- Эксперт
-
- Сообщения: 5587
- Зарегистрирован: 17 сен 2010, 12:58
- Пол:
- Награды: 14
-
-
- Поблагодарили: 2185 раз.
|
AlexSpl » 22 мар 2021, 03:50
Death Ripple, Destroy Undead, Armageddon + примитивное отражение AoE (для тестов) - Код: Выделить всё
#define _CRT_SECURE_NO_WARNINGS #include "stdafx.h" #include "..\..\HotA\homm3.h"
Patcher* _P; PatcherInstance* _PI;
bool showMagicMirrorAnim, showMagicResistAnim, showSpellAnim, redirectAreaSpell; bool needMagicMirrorAnim[2][20], needMagicResistAnim[2][20], needSpellAnim[2][20]; int stackIndex;
#define SPL_DEATH_RIPPLE 24 #define AnimateStack (((bool(*)[20])&o_BattleMgr->Field<bool>(0x547C)))
enum E {E_MASS, E_BERSERK, E_AREA, E_DEATH_RIPPLE, E_DESTROY_UNDEAD, E_ARMAGEDDON, E_SIZE}; enum A {A_REFLECTSPELL_HOOK, A_MASSREFLECTION_HOOK, A_REFLECTSPELL_SKIP, A_REFLECTSPELL_DEFAULT, A_MASSANIMATION};
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 };
_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 magicMirrorLucky(const _BattleStack_* stack, const int spell) { int effSchoolLevel = CALL_3(int, __thiscall, 0x4E52F0, o_BattleMgr->hero[stack->side], SPL_MAGIC_MIRROR, o_BattleMgr->spec_terr_type); return !(o_Spell[spell].flags & SPF_FRIENDLY_HAS_MASS) && (stack->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) && stack->active_spell_duration[SPL_MAGIC_MIRROR] > 0 && Randint(1, 100) <= o_Spell[SPL_MAGIC_MIRROR].effect[effSchoolLevel]; }
_BattleStack_* getEnemyStackToRedirectTo(const _BattleStack_* stack, const int spell) { // В целях демонстрации работы просто перенаправим заклинание на первый доступный вражеский отряд int sideFoe = 1 - stack->side; _BattleStack_* stackFoe = stackIndex < o_BattleMgr->stacks_count[sideFoe] && !o_BattleMgr->stack[sideFoe][stackIndex].is_killed ? &o_BattleMgr->stack[sideFoe][stackIndex] : 0;
++stackIndex; return stackFoe; }
int __stdcall newTarget(LoHook* h, HookContext* c) { // Если нужно изменить оригинальный выбор цели, пропишите указатель на отряд в eaх // (!) Обязательно, чтобы работало отражение синглкаста AI (!) // В этом примере заклинание перенаправляется на вражеский отряд с индексом 0, даже если он погиб, // так что здесь нужно написать свой алгоритм выбора цели c->eax = (int)&o_BattleMgr->stack[o_BattleMgr->current_side][0]; return EXEC_DEFAULT; }
int __stdcall magicMirrorAI(LoHook* h, HookContext* c) { _BattleStack_* stack = (_BattleStack_*)c->edi; if ( stack ) { _Hero_* hero = o_BattleMgr->hero[1 - o_BattleMgr->current_side]; int spell = *(int*)(c->ebp + 8); int effSchoolLevel = CALL_3(int, __thiscall, 0x4E52F0, hero, SPL_MAGIC_MIRROR, o_BattleMgr->spec_terr_type); // Если герой - 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; }
int __stdcall skipOrigSound_dataInit(LoHook* h, HookContext* c) { int spell = *(int*)(c->ebp + 8); char switchCase = ((char*)0x5A2B48)[spell - 10];
// Если не синглкаст if ( !c->edi ) { showMagicMirrorAnim = false; showMagicResistAnim = false; showSpellAnim = false; redirectAreaSpell = false; memset(needMagicMirrorAnim, false, 40); memset(needMagicResistAnim, false, 40); memset(needSpellAnim, false, 40); stackIndex = 0;
if ( spell == SPL_DEATH_RIPPLE || spell == SPL_DESTROY_UNDEAD || spell == SPL_ARMAGEDDON || switchCase == 17 || switchCase == 24 || o_BattleMgr->ShouldNotRenderBattle() ) { c->return_address = 0x5A0646; return NO_EXEC_DEFAULT; } } return EXEC_DEFAULT; }
int __stdcall reflectSpell(LoHook* h, HookContext* c) { _Hero_* hero; _BattleStack_* stack; int spell, spellPower, schoolLevel; int casterSide = o_BattleMgr->current_side;
_ptr_ addr = h->GetAddress(); if ( addr == hAddr[A_REFLECTSPELL_HOOK][E_MASS] ) { stack = (_BattleStack_*)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 = *(_BattleStack_**)(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 = (_BattleStack_*)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 = (_BattleStack_*)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 = (_BattleStack_*)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 = (_BattleStack_*)c->esi; spell = SPL_ARMAGEDDON; c->return_address = hAddr[A_REFLECTSPELL_SKIP][E_ARMAGEDDON]; } if ( stack ) { bool isSpellSpecial = spell == SPL_FROST_RING || spell == SPL_FIREBALL || spell == SPL_INFERNO || spell == SPL_METEOR_SHOWER || spell == SPL_DEATH_RIPPLE || spell == SPL_DESTROY_UNDEAD || spell == SPL_ARMAGEDDON;
if ( magicMirrorLucky(stack, spell) ) { showMagicMirrorAnim = true; needMagicMirrorAnim[stack->side][stack->index_on_side] = true; redirectAreaSpell = addr == hAddr[A_REFLECTSPELL_HOOK][E_AREA]; if ( isSpellSpecial ) return NO_EXEC_DEFAULT;
// Если сработало Magic Mirror, перенаправляем заклинание на вражеский отряд _BattleStack_* stackFoe = getEnemyStackToRedirectTo(stack, spell); if ( stackFoe ) { // Определяем, сработает ли резист против отражённого заклинания int hitPercentage = (int)(100 * CALL_7(float, __thiscall, 0x5A83A0, o_BattleMgr, spell, casterSide, stackFoe, 1, 1, *(int*)(c->ebp + 0x1C)));
if ( Randint(1, 100) <= hitPercentage ) { showSpellAnim = true; needSpellAnim[stackFoe->side][stackFoe->index_on_side] = true; CALL_5(void, __thiscall, 0x444610, stackFoe, spell, spellPower, schoolLevel, hero); } else if ( hitPercentage ) { showMagicResistAnim = true; needMagicResistAnim[stackFoe->side][stackFoe->index_on_side] = true; } } } else { if ( Randint(1, 100) <= c->eax ) { showSpellAnim = true; needSpellAnim[stack->side][stack->index_on_side] = true;
if ( isSpellSpecial ) { c->return_address = getRA(spell); return NO_EXEC_DEFAULT; } // Если не сработал резист, применяем эффект заклинания CALL_5(void, __thiscall, 0x444610, stack, spell, spellPower, schoolLevel, hero); } else if ( c->eax ) { showMagicResistAnim = true; needMagicResistAnim[stack->side][stack->index_on_side] = true;
if ( isSpellSpecial ) return NO_EXEC_DEFAULT; } } } return NO_EXEC_DEFAULT; }
int __stdcall massReflection(LoHook* h, HookContext* c) { _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] ) c->return_address = hAddr[A_MASSANIMATION][E_DEATH_RIPPLE]; if ( addr == hAddr[A_MASSREFLECTION_HOOK][E_DESTROY_UNDEAD] ) { c->edi = *(int*)(c->ebp - 0x10); c->return_address = hAddr[A_MASSANIMATION][E_DESTROY_UNDEAD]; }
// Проигрываем массовую анимацию и звук 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); }
// Проигрываем массовую анимацию и звук резиста if ( showMagicResistAnim ) { CALL_3(void, __fastcall, 0x59A890, "MagicRes.wav", -1, 3); CALL_4(void, __thiscall, 0x5A6AD0, o_BattleMgr, &needMagicResistAnim, 78, 0); }
if ( addr == hAddr[A_MASSREFLECTION_HOOK][E_ARMAGEDDON] ) { memcpy(AnimateStack, needSpellAnim, 40); return EXEC_DEFAULT; }
// Проигрываем массовую анимацию и звук кастуемого заклинания 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, 0); } return NO_EXEC_DEFAULT; }
void __stdcall areaReflection(HiHook* h, _BattleMgr_* battleMgr, int a1, int spell, int a3, int a4) { memcpy(AnimateStack, needSpellAnim, 40); CALL_5(void, __thiscall, h->GetDefaultFunc(), battleMgr, a1, spell, a3, a4); // Проигрываем массовую анимацию и звук резиста 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); }
// Если заклинание отражено if ( redirectAreaSpell ) { showMagicMirrorAnim = false; showMagicResistAnim = false; memset(needMagicMirrorAnim, false, 40); memset(needMagicResistAnim, false, 40);
CALL_5(void, __thiscall, h->GetDefaultFunc(), battleMgr, o_BattleMgr->stack[o_BattleMgr->current_side][0].hex_ix, spell, a3, a4);
// Проигрываем массовую анимацию и звук резиста 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 playMagicMirrorSound(LoHook* h, HookContext* c) { CALL_3(void, __fastcall, 0x59A890, o_Spell[SPL_MAGIC_MIRROR].wav_name, -1, 3); 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, skipOrigSound_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); } }
return TRUE; }
Попробовал собрать все адреса в один массив. Надеюсь, ничего не напутал. Во-первых, текст плагина становится короче, а во-вторых, если нужно будет поменять адрес хука, например, достаточно будет исправить его в одном месте, а не искать по всему коду. Также поменял порядок анимации для AoE спеллов: теперь сначала идёт анимация заклинания, потом урона, затем резиста и только в конце - зеркала. Так как планируется отражать AoE заклинания, то логично проигрывать анимацию зеркала после анимации резиста, потому что сразу после анимации зеркала начинается точно такая же последовательность анимаций, но уже для отражённого спелла. * * * Добавил тривиальное отражение AoE: на стек противника с индексом 0. Как бы скелет плагина готов. Осталось написать грамотные алгоритмы выбора цели (в том числе, эпицентра отражённого AoE закла*), оптимизировать код и исправить возможные баги * Здесь Вам всего лишь нужно выбрать гекс-эпицентр отражённого заклинания и передать его вот сюда: - Код: Выделить всё
CALL_5(void, __thiscall, h->GetDefaultFunc(), battleMgr, o_BattleMgr->stack[o_BattleMgr->current_side][0].hex_ix, spell, a3, a4);
Поле имеет размер 17 x 11 гексов. 0-я и 16-я вертикали - для боевых машин. Нужно написать функцию, которая будет проходить по всем гексам поля, и вычислять кол-во вражеских отрядов в нужном радиусе от текущего гекса.
|