Единственное что тут не решено: это нет поддержки миникарты. Я не решал эту проблему по двум причинам:
1. Миникарта корректно работает в HD-моде (там Бараторч ёё решил (видимо интегрировал из Хоты)). А так как это плагин под HD, то по факту и решать эту проблему не нужно.
2. Решение этой проблемы достаточно сложное, и нужно некисло так переписать большой кусок нескольких функций обработки миникарты. Учитывая пункт 1 - я посчитал, что "игра не стоит свеч".
- Код: Выделить всё
#include "stdafx.h"
#include "..\..\include\homm3.h"
char myString[1024];
#define MyString (char*)myString
Patcher* _P;
PatcherInstance* _PI;
///////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////// КОНСТАНТЫ ///////////////////////////////////////////////
#define MAPSIZE_S 36
#define MAPSIZE_M 72
#define MAPSIZE_L 108
#define MAPSIZE_XL 144
#define MAPSIZE_H 180
#define MAPSIZE_XH 216
#define MAPSIZE_G 252
#define BTTNID_S 281
#define BTTNID_M 282
#define BTTNID_L 283
#define BTTNID_XL 284
#define BTTNID_U 285
#define BTTNID_PCX 1102
#define BTTNID_H 1103
#define BTTNID_XH 1104
#define BTTNID_G 1105
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
#define SmallFont_FNT (*(char**)0x4EEAF8)
char* RanSizS_DEF = "RanSizS.def";
char* RanSizM_DEF = "RanSizM.def";
char* RanSizL_DEF = "RanSizL.def";
char* RanSizXL_DEF = "RanSizX.def";
char* RanSizHG_DEF = "RanSizHG.def";
char* RanSizXH_DEF = "RanSizXH.def";
char* RanSizGT_DEF = "RanSizGT.def";
char* RanSizU_DEF = "RanUndr.def";
// переменная для проверки стартовала ли игра (т.е. видима ли уже карта)
// например диалог повышения уровня при старте карты пропускается из-за неё
#define o_StartGame_SkipDialogs (*(_int_*)0x698450)
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
int __stdcall Y_NewScenarioDlg_Proc(HiHook* hook, _NewScenarioDlg_* sDlg, _EventMsg_* msg)
{
// делаем ПКМ на кнопках размера карты при создании "случайной карты"
if (msg->type == MT_MOUSEBUTTON) {
if(msg->subtype == MST_RBUTTONDOWN) {
if ( msg->item_id >= BTTNID_S && msg->item_id <= BTTNID_XL ) {
int size = (msg->item_id -BTTNID_S +1) *MAPSIZE_S;
sprintf(MyString, "{%s:} %d x %d", o_GENRLTXT_TXT->GetString(754), size, size);
b_MsgBox(MyString, 4);
return 1;
}
if (msg->item_id >= BTTNID_H && msg->item_id <= BTTNID_G ) {
int size = (msg->item_id -BTTNID_H +5) *MAPSIZE_S;
sprintf(MyString, "{%s:} %d x %d", o_GENRLTXT_TXT->GetString(754), size, size);
b_MsgBox(MyString, 4);
return 1;
}
//sprintf(MyString, "id: %d", msg->item_id);
//b_MsgBox(MyString, 4);
//return 1;
}
}
return CALL_2(int, __thiscall, hook->GetDefaultFunc(), sDlg, msg);
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
// вырезаем создание кнопок игрой и создаём их сами
int __stdcall Y_NewScenarioDlg_RandomMap_CreateButtons(LoHook* h, HookContext* c)
{
// получаем структуру диалога
_NewScenarioDlg_* dlg = (_NewScenarioDlg_*)c->ebx;
// создаём текст (размер карты)
dlg->AddItemToOwnArrayList(_DlgStaticText_::Create(58, 82, 99, 31, o_GENRLTXT_TXT->GetString(754), SmallFont_FNT, 1, 280, ALIGN_H_CENTER | ALIGN_V_CENTER, 0));
// перекрываем этот текс и его обводку голубым фоном
dlg->AddItemToOwnArrayList(_DlgStaticPcx8_::Create(56, 78, 102, 38, BTTNID_PCX, "DiBoxBlu.pcx"));
// координаты кнопок
int x = 57; int xh = 44; int xd = 43;
int y = 81; int yh = 33;
// стандартные кнопки: S, M, L, XL
dlg->AddItemToOwnArrayList(_DlgButton_::Create(x + xd*0, y, xh, yh, BTTNID_S, RanSizS_DEF, 0, 1, 0, 0, 2));
dlg->AddItemToOwnArrayList(_DlgButton_::Create(x + xd*1, y, xh, yh, BTTNID_M, RanSizM_DEF, 0, 1, 0, 0, 2));
dlg->AddItemToOwnArrayList(_DlgButton_::Create(x + xd*2, y, xh, yh, BTTNID_L, RanSizL_DEF, 0, 1, 0, 0, 2));
dlg->AddItemToOwnArrayList(_DlgButton_::Create(x + xd*3, y, xh, yh, BTTNID_XL, RanSizXL_DEF, 0, 1, 0, 0, 2));
// новые кнопки: H, XH, G
dlg->AddItemToOwnArrayList(_DlgButton_::Create(x + xd*4, y, xh, yh, BTTNID_H, RanSizHG_DEF, 0, 1, 0, 0, 2));
dlg->AddItemToOwnArrayList(_DlgButton_::Create(x + xd*5, y, xh, yh, BTTNID_XH, RanSizXH_DEF, 0, 1, 0, 0, 2));
dlg->AddItemToOwnArrayList(_DlgButton_::Create(x + xd*6, y, xh, yh, BTTNID_G, RanSizGT_DEF, 0, 1, 0, 0, 2));
// кнопка подземелья
dlg->AddItemToOwnArrayList(_DlgButton_::Create(x + xd*7, y, xh, yh, BTTNID_U, RanSizU_DEF, 0, 1, 0, 0, 2));
// пропускаем оригинальный код игры
c->return_address = 0x57D6CC;
return NO_EXEC_DEFAULT;
}
int DlgItem_SendCommand(_Dlg_* dlg, int itemID, int enable)
{
if (enable) // DlgItem_Send5Cmd2Item
return CALL_3(int, __thiscall, 0x5FF490, dlg, itemID, 16);
else // DlgItem_Send6Cmd2Item
return CALL_3(int, __thiscall, 0x5FF520, dlg, itemID, 16);
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
char Y_NewScenarioDlg_RandomMap_CALL_UpdateButtons(_NewScenarioDlg_* dlg, int mapSize)
{
*(int*)((int)dlg + 6304) = mapSize;
CALL_1(char, __thiscall, 0x57F240, dlg);
dlg->Redraw(0);
CALL_1(signed int, __thiscall, 0x584820, dlg);
return 0;
}
int __stdcall Y_NewScenarioDlg_RandomMap_UpdateButtons(LoHook* h, HookContext* c)
{
// получаем структуру диалога
_NewScenarioDlg_* dlg = (_NewScenarioDlg_*)c->esi;
// отключаем все старые кнопки
DlgItem_SendCommand(dlg, BTTNID_S, 0);
DlgItem_SendCommand(dlg, BTTNID_M, 0);
DlgItem_SendCommand(dlg, BTTNID_L, 0);
DlgItem_SendCommand(dlg, BTTNID_XL, 0);
// отключаем все новые кнопки
DlgItem_SendCommand(dlg, BTTNID_H, 0);
DlgItem_SendCommand(dlg, BTTNID_XH, 0);
DlgItem_SendCommand(dlg, BTTNID_G, 0);
// тут код включения нужной кнопки
// в зависимости от размера карты 0x57F270
int mapSize = *(int*)((int)dlg + 6304);
if (mapSize == MAPSIZE_S)
DlgItem_SendCommand(dlg, BTTNID_S, 1);
if (mapSize == MAPSIZE_M)
DlgItem_SendCommand(dlg, BTTNID_M, 1);
if (mapSize == MAPSIZE_L)
DlgItem_SendCommand(dlg, BTTNID_L, 1);
if (mapSize == MAPSIZE_XL)
DlgItem_SendCommand(dlg, BTTNID_XL, 1);
if (mapSize == MAPSIZE_H)
DlgItem_SendCommand(dlg, BTTNID_H, 1);
if (mapSize == MAPSIZE_XH)
DlgItem_SendCommand(dlg, BTTNID_XH, 1);
if (mapSize == MAPSIZE_G)
DlgItem_SendCommand(dlg, BTTNID_G, 1);
c->return_address = 0x57F2B6;
return NO_EXEC_DEFAULT;
}
int __stdcall Y_NewScenarioDlg_RandomMap_CallUpdateButtons(HiHook* hook, _NewScenarioDlg_* dlg, _EventMsg_* msg, int a3, int a4)
{
if (msg->item_id == BTTNID_H)
return Y_NewScenarioDlg_RandomMap_CALL_UpdateButtons(dlg, MAPSIZE_H);
if (msg->item_id == BTTNID_XH)
return Y_NewScenarioDlg_RandomMap_CALL_UpdateButtons(dlg, MAPSIZE_XH);
if (msg->item_id == BTTNID_G)
return Y_NewScenarioDlg_RandomMap_CALL_UpdateButtons(dlg, MAPSIZE_G);
return CALL_4(int, __thiscall, hook->GetDefaultFunc(), dlg, msg, a3, a4);
}
// показать кнопки
int __stdcall Y_NewScenarioDlg_RandomMap_ShowButtons(LoHook* h, HookContext* c)
{
// получаем структуру диалога
_NewScenarioDlg_* dlg = (_NewScenarioDlg_*)c->esi;
_DlgItem_* item;
item = dlg->GetItem(BTTNID_PCX);
if (item) item->Show();
item = dlg->GetItem(BTTNID_H);
if (item) item->Show();
item = dlg->GetItem(BTTNID_XH);
if (item) item->Show();
item = dlg->GetItem(BTTNID_G);
if (item) item->Show();
return EXEC_DEFAULT;
}
// скрыть кнопки
int __stdcall Y_NewScenarioDlg_RandomMap_HideButtons(LoHook* h, HookContext* c)
{
// получаем структуру диалога
_NewScenarioDlg_* dlg = (_NewScenarioDlg_*)c->esi;
_DlgItem_* item;
item = dlg->GetItem(BTTNID_PCX);
if (item) item->Hide();
item = dlg->GetItem(BTTNID_H);
if (item) item->Hide();
item = dlg->GetItem(BTTNID_XH);
if (item) item->Hide();
item = dlg->GetItem(BTTNID_G);
if (item) item->Hide();
return EXEC_DEFAULT;
}
// патч: фикс вылета при загрузке/старте карты
int __stdcall Y_Fix_OpenArea(LoHook* h, HookContext* c)
{
if (o_StartGame_SkipDialogs)
{
c->return_address = 0x49D026;
return NO_EXEC_DEFAULT;
}
return EXEC_DEFAULT;
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
void StartPlugin()
{
_PI->WriteHiHook(0x587FD0, SPLICE_, EXTENDED_, THISCALL_, Y_NewScenarioDlg_Proc);
// вырезаем создание кнопок игрой и создаём их сами
_PI->WriteCodePatch(0x57D49B, "%n", 2);
_PI->WriteLoHook(0x57D4A6, Y_NewScenarioDlg_RandomMap_CreateButtons);
// показать кнопки
_PI->WriteLoHook(0x5801E1, Y_NewScenarioDlg_RandomMap_ShowButtons);
// скрыть кнопки
_PI->WriteLoHook(0x5820A6, Y_NewScenarioDlg_RandomMap_HideButtons);
// подсветка нужной кнопки случайной карты
_PI->WriteLoHook(0x57F258, Y_NewScenarioDlg_RandomMap_UpdateButtons);
// нажатия кнопок обрабатывать тут 0x586938 -> 0x586FEB
_PI->WriteHiHook(0x586880, SPLICE_, EXTENDED_, THISCALL_, Y_NewScenarioDlg_RandomMap_CallUpdateButtons);
// читы: радиус открытия/закрытия карты
_PI->WriteDword(0x4026F9 +1, 356);
_PI->WriteDword(0x402750 +1, 356);
_PI->WriteDword(0x4F4B56 +1, 356);
// патч: фикс вылета при загрузке/старте карты
_PI->WriteLoHook(0x49CFFA, Y_Fix_OpenArea);
// решение проблемы вылета игры при загрузке сохранения
// суть: __int16 в __uint16 (делаем беззнаковое)
_PI->WriteByte(0x49B96F +1, 0xB7); // загрузка (change MOVSX to MOVZX)
_PI->WriteByte(0x49BAA2, 0x76); // сохранение (change JLE to JBE)
_PI->WriteByte(0x49BAB4 +1, 0xB7); // сохранение (change MOVSX to MOVZX)
_PI->WriteByte(0x49BABB, 0x72); // сохранение (change JL to JB)
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
BOOL APIENTRY DllMain ( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
static _bool_ initialized = 0;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
if (!initialized)
{
initialized = 1;
_P = GetPatcher();
_PI = _P->CreateInstance("igrik.XXL");
StartPlugin();
}
break;
case DLL_PROCESS_DETACH:
if (initialized)
initialized = 0;
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
PS: и ещё один важнйый момент: данный код предназначен только для генерации случайных карт.