Добавил проверку на посещение Склепа
Да, я тоже его добавил еще в коде ранее. Все работает. Правда заметил, что Вы в своем коде
DwordAt заменили на
*(int*). Какой в этом смысл?
В общем немного причесал ваш код:
1. В банках существ после посещения и отказа, по правому клику нет необходимости выводить дополнительно кол-во по отрядам, ибо в оригинале там вывод кол-ва идет итак. Нет смысла дублировать. Так что этот кусок кода я закоментил. То есть вывод кол-ва нужен был именно во время посещения банка в диалоге.
2. Кол-во существ в жилищах как раз нужно точное в отличии от банков существ, а потому пришлось убрать обращение к функции
getInterval, то есть
getInterval(dwelling->creature_counts[i]) заменить на
to_string(dwelling->creature_counts[i]).
3. Сам вывод немного изменил и приукрасил для удобства восприятия и выделил доступное кол-во.
4. Проверил на Фабрике големов, это, вероятно, единственное жилище, где можно нанять больше одного типа существ. Все ОК.
Осталось разобраться с Refugee Camp:По поводу Refugee Camp (Лагерь беженцев), вот здесь по ПКМ даже до посещения отображается тип и кол-во доступных существ для найма. А нужно, чтобы это становилось доступно только после посещения.
но с Refugee Camp есть проблема: у них нет владельца и их нельзя отметить как посещённые, поэтому нужно либо самостоятельно отмечать владельца в неиспользуемых полях структуры (что всегда опасно), либо заводить свой массив или список владельцев Refugee Camp.
То, что опасно, лучше всегда обходить стороной, а то потом пойдут вылеты и различного рода проблемы. Я думаю второй вариант будет предпочтительней, каждого игрока (или может только игрока-человека) посетившего Refugee Camp кладем в массив и делаем для него после посещения в диалоге по ПКМ доступным тип и кол-во существ доступных для найма.
Кстати, заметил очень странную вещь, которую пока не могу объяснить. Для Вампиров (Vampires) текст с количеством при посещении Refugee Camp не показывается (SoD 3.2 EN). Пока списываю это на то, как игра выбирает размер диалога в зависимости от размера текста.
Надо бы еще проверить на русской версии с русскими шрифтами.
У меня все ОК.
Это не HotA, а наш плагин. Просто я вывод сделал как в HotA. Но я тестирую как плагин к HD-моду, а мод все эти диалоги в жилищах "Хотите нанять таких-то существ?" для удобства и ускорения процесса игры убирает.
А каким образом вы без мода подцепили этот плагин к оригиналу, как ASI-плагин?
Я так понимаю HotA вы вообще не запускаете/не играете?
- Код: Выделить всё
#define _CRT_SECURE_NO_WARNINGS
#include "stdafx.h"
#include "HotA\homm3.h"
#include <string>
using namespace std;
Patcher* _P;
PatcherInstance* _PI;
static _bool_ plugin_On = 0;
#define o_CreatureInfo ((_CreatureInfo_*)0x6703B8)
string getInterval(int n)
{
string str;
if (n < 1) str = "0"; else
if (n < 5) str = "1-4"; else
if (n < 10) str = "5-9"; else
if (n < 20) str = "10-19"; else
if (n < 50) str = "20-49"; else
if (n < 100) str = "50-99"; else
if (n < 250) str = "100-249"; else
if (n < 500) str = "250-499"; else
if (n < 1000) str = "500-999"; else str = "1000+";
return str;
}
_MapItem_* mapItem;
int __stdcall saveObjEntranceAddr(LoHook* h, HookContext* c)
{
mapItem = (_MapItem_*)c->eax;
return EXEC_DEFAULT;
}
// Получаем указатель на _Dwelling_ по ID жилища существ
_Dwelling_* GetDwelling(_int_ dwellingId) {
return (_Dwelling_ *)(o_GameMgr->Field<int>(0x4E39C) + 92 * dwellingId);
}
int __stdcall Crypt_SkipMsgIfVisited(LoHook* h, HookContext* c)
{
if (*(int*)*(int*)(c->ebp + 0xC) & 0x2000000)
{
c->return_address = 0x4AC1E5;
return NO_EXEC_DEFAULT;
}
return EXEC_DEFAULT;
}
int __stdcall showGuards(LoHook* h, HookContext* c)
{
// Получаем состояние банка
_MapItem_* mapItem = (_MapItem_*)*(int*)(c->ebp + 0xC);
if (mapItem) // Перестраховываемся, но можно и без этой проверки
{
_CrBankState_* bankState = CALL_1(_CrBankState_*, __fastcall, 0x405D80, mapItem);
string str = (char*)c->ecx; // Оригинальное сообщение
str += "\n";
for (int i = 0; i < bankState->defenders.GetStacksCount(); ++i)
str = str + "\n" + o_CreatureInfo[bankState->defenders.type[i]].name_plural + ": " +
getInterval(bankState->defenders.count[i]);
// Передаём адрес текстового буфера в качестве аргумента для диалога
sprintf(o_TextBuffer, "%s", str.c_str());
c->ecx = (int)o_TextBuffer;
mapItem->SetAsVisited(o_GameMgr->GetMeID());
}
return EXEC_DEFAULT;
}
int __stdcall showMonsters(LoHook* h, HookContext* c)
{
// Получаем состояние жилища
_MapItem_* mapItem = (_MapItem_*)*(int*)(c->ebp + 0xC);
if (mapItem) // Перестраховываемся, но можно и без этой проверки
{
string str = (char*)c->ecx; // Оригинальное сообщение
str += "\n";
// Получаем состояние жилища существ
_Dwelling_* dwelling = GetDwelling(mapItem->setup);
// Если мы владельцы, показываем количество существ
if (dwelling && dwelling->owner_ix == o_GameMgr->GetMeID())
{
string str = (char*)c->ecx; // Оригинальное сообщение
str += "\n\n";
str += "Доступные существа:";
for (int i = 0; i < 4; ++i)
if (dwelling->creature_types[i] != ID_NONE)
str = str + "\n" + o_CreatureInfo[dwelling->creature_types[i]].name_plural + " — " + "{" +
to_string(dwelling->creature_counts[i]) + "}";
// Передаём адрес текстового буфера в качестве аргумента для диалога
sprintf(o_TextBuffer, "%s", str.c_str());
c->ecx = (int)o_TextBuffer;
}
}
return EXEC_DEFAULT;
}
int __stdcall showRefugeeCamp(LoHook* h, HookContext* c)
{
// Получаем состояние жилища
_MapItem_* mapItem = (_MapItem_*)*(int*)(c->ebp + 0xC);
if (mapItem) // Перестраховываемся, но можно и без этой проверки
{
string str = (char*)c->ecx; // Оригинальное сообщение
// Передаём адрес текстового буфера в качестве аргумента для диалога
sprintf(o_TextBuffer, "%s\n\nДоступные существа:\n%s — {%d}", str.c_str(), o_CreatureInfo[mapItem->os_type].name_plural, mapItem->setup);
c->ecx = (int)o_TextBuffer;
}
return EXEC_DEFAULT;
}
int __stdcall showGuardsRMB(LoHook* h, HookContext* c)
{
if (mapItem)
{
/* // Банки существ
if (mapItem->IsVisited(o_GameMgr->GetMeID()))
{
// mapItem->object_type = 0x10 - стандартные банки, 0x18 - Derelict Ship, 0x19 - Dragon Utopia, 0x54 - Crypt, 0x55 - Shipwreck
int objID = mapItem->object_type;
if (mapItem->object_type == 0x10 || mapItem->object_type == 0x18 || mapItem->object_type == 0x19 ||
mapItem->object_type == 0x54 || mapItem->object_type == 0x55)
{
// Получаем состояние банка
_CrBankState_* bankState = CALL_1(_CrBankState_*, __fastcall, 0x405D80, mapItem);
string str = (char*)c->ecx; // Оригинальное сообщение
str += "\n";
for (int i = 0; i < bankState->defenders.GetStacksCount(); ++i)
str = str + "\n" + o_CreatureInfo[bankState->defenders.type[i]].name_plural + ": " +
getInterval(bankState->defenders.count[i]);
// Передаём адрес текстового буфера в качестве аргумента для диалога
sprintf(o_TextBuffer, "%s", str.c_str());
c->ecx = (int)o_TextBuffer;
}
}
*/
// Жилища существ
if (mapItem->object_type == 0x11 || mapItem->object_type == 0x14)
{
// Получаем состояние жилища существ
_Dwelling_* dwelling = GetDwelling(mapItem->setup);
// Если мы владельцы, показываем количество существ
if (dwelling && dwelling->owner_ix == o_GameMgr->GetMeID())
{
string str = (char*)c->ecx; // Оригинальное сообщение
str += "\n\n";
str += "Доступные существа:";
for (int i = 0; i < 4; ++i)
if (dwelling->creature_types[i] != ID_NONE)
str = str + "\n" + o_CreatureInfo[dwelling->creature_types[i]].name_plural + " — " + "{" +
to_string(dwelling->creature_counts[i]) + "}";
// Передаём адрес текстового буфера в качестве аргумента для диалога
sprintf(o_TextBuffer, "%s", str.c_str());
c->ecx = (int)o_TextBuffer;
}
}
// Лагерь беженцев
if (mapItem->object_type == 0x4E)
{
string str = (char*)c->ecx; // Оригинальное сообщение
// Передаём адрес текстового буфера в качестве аргумента для диалога
sprintf(o_TextBuffer, "%s\n\nДоступные существа:\n%s — {%d}", str.c_str(), o_CreatureInfo[mapItem->os_type].name_plural, mapItem->setup);
c->ecx = (int)o_TextBuffer;
}
}
return EXEC_DEFAULT;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
if (DLL_PROCESS_ATTACH == ul_reason_for_call)
{
if (!plugin_On)
{
plugin_On = 1;
_P = GetPatcher();
_PI = _P->CreateInstance((char*)"HD.Plugin.ShowGuards");
// Сообщения по правому клику
_PI->WriteLoHook(0x415AEE, showGuardsRMB);
// Сообщения при посещении
_PI->WriteLoHook(0x4A13E6, showGuards); // стандартных банков
_PI->WriteLoHook(0x4A1E56, showGuards); // Dragon Utopia
_PI->WriteLoHook(0x4AC1B9, showGuards); // Crypt, Derelict Ship, Shipwreck
_PI->WriteLoHook(0x4A18FF, showMonsters); // жилища существ 1-го уровня
_PI->WriteLoHook(0x4A435C, showRefugeeCamp); // Refugee Camp
_PI->WriteLoHook(0x413917, saveObjEntranceAddr); // Сохраняем адрес тайла-входа в объект
_PI->WriteLoHook(0x4AC19D, Crypt_SkipMsgIfVisited); // убираем диалог обыска при посещении Склепа и Кораблей, когда они уже разграблены
}
}
return TRUE;
}