-
Rolex
имя: Alex
- Ветеран
-
- Сообщения: 898
- Зарегистрирован: 22 сен 2020, 18:58
- Откуда: УКРАИНА
- Пол:
- Поблагодарили: 53 раз.
|
Rolex » 03 мар 2021, 16:12
Ясно. Ну тогда публикую весь код по охране, который у меня получился. Надеюсь, вы поможете его доработать. Как я понял нам нужно будет заменить вашу функцию getStacksCount на эту. Плюс у нас же еще есть upgradedStack и по идеи она и ее должна заменить. В обе функции вы передаете коеф дабл k и 3 чара x, y, z. Там же другие аргументы принимаються какая-то константа, указатель на героя, и два инта - тип и сумма. Я использую H3API.hpp - single header. - Код: Выделить всё
#define _CRT_SECURE_NO_WARNINGS #include "stdafx.h" #include "HotA\homm3.h" #include <string> #include "H3API\single_header\H3API.hpp"
using namespace std; using namespace h3;
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 Pyramid_SkipMsgIfVisited(LoHook* h, HookContext* c) { // Получаем состояние пирамиды _MapItem_* mapItem = (_MapItem_*)*(int*)(c->ebp + 0xC);
if (mapItem->setup & 1) return EXEC_DEFAULT;
c->return_address = 0x4A3F22; return NO_EXEC_DEFAULT; }
int getStacksCount(double k, char x, char y, char z) { __int64 devCoef[] = { 0x5C6F80EB, 0xC8374AB9, 0x73D409E3, 0xBD38DECE };
int n = 2; if (k < 0.50) n = 7; else if (k < 0.67) n = 6; else if (k < 1.00) n = 5; else if (k < 1.50) n = 4; else if (k < 2.00) n = 3;
// Deviation __int64 splitValue = (((devCoef[0] * x + devCoef[1] * y + devCoef[2] * z + devCoef[3]) >> 0x10) & 0x7FFF) % 100 + 1; if (splitValue <= 20) --n; if (splitValue >= 80 && n < 7) ++n;
return n; }
bool upgradedStack(double k, char x, char y, char z) { __int64 upgCoef[] = { 0x0BB0E93F, 0x375E43D5, 0x14CD2E57, 0x8014BA59 };
return (((upgCoef[0] * x + upgCoef[1] * y + upgCoef[2] * z + upgCoef[3]) >> 0x10) & 0x7FFF) % 100 < 50; }
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 showAbandonedMine(LoHook* h, HookContext* c) { // Получаем состояние шахты _MapItem_* mapItem = (_MapItem_*)*(int*)(c->ebp + 8);
// Если есть охрана if (*(char*)(o_GameMgr->Field<int>(0x4E38C) + (mapItem->setup << 6)) < 0) { // Если герой выбран if (o_ActivePlayer->selected_hero_id > ID_NONE && o_ActivePlayer->selected_hero_id < 156) { _Hero_* hero = o_GameMgr->GetHero(o_ActivePlayer->selected_hero_id); int heroArmyPower = CALL_1(int, __fastcall, 0x44A950, &hero->army);
_Army_* defArmy = (_Army_*)(o_GameMgr->Field<int>(0x4E38C) + (mapItem->setup << 6) + 4); int defArmyPower = CALL_1(int, __fastcall, 0x44A950, defArmy);
if (defArmyPower) // На всякий (чтобы избежать неожиданного деления на 0) { double k = (double)heroArmyPower / defArmyPower;
// Вычисляем координаты входа int mapSize = o_AdvMgr->map->size; int pos = mapItem - o_AdvMgr->map->items; char x = pos % mapSize; pos /= mapSize; char y = pos % mapSize; char z = pos / mapSize;
// Получаем количество стеков, на которое разбилась охрана int stackCount = getStacksCount(k, x, y, z);
// Выясняем, есть ли улучшенный стек char* upgStr = upgradedStack(k, x, y, z) ? "Yes" : "No";
string str = (char*)c->ecx; // Оригинальное сообщение str += "\n";
int position = 0;
for (int i = 0; i < defArmy->count[0] % stackCount; ++i) { position++; if (upgStr == "Yes" && position == stackCount / 2 + 1) str = str + "\n" + o_CreatureInfo[CALL_1(int, __fastcall, 0x47AAD0, defArmy->type[0])].name_plural + ": "; else str = str + "\n" + o_CreatureInfo[defArmy->type[0]].name_plural + ": ";
str = str + to_string(defArmy->count[0] / stackCount + 1); }
for (int i = 0; i < stackCount - (defArmy->count[0] % stackCount); ++i) { position++; if (upgStr == "Yes" && position == stackCount / 2 + 1) str = str + "\n" + o_CreatureInfo[CALL_1(int, __fastcall, 0x47AAD0, defArmy->type[0])].name_plural + ": "; else str = str + "\n" + o_CreatureInfo[defArmy->type[0]].name_plural + ": ";
str = str + to_string(defArmy->count[0] / stackCount); }
// Передаём адрес текстового буфера в качестве аргумента для диалога sprintf(o_TextBuffer, "%s", str.c_str());
c->ecx = (int)o_TextBuffer; } } }
return EXEC_DEFAULT; }
int __stdcall showPyramid(LoHook* h, HookContext* c) { // Получаем состояние пирамиды _MapItem_* mapItem = (_MapItem_*)*(int*)(c->ebp + 0xC);
// Если есть охрана if (mapItem->setup & 1) { // Если герой выбран if (o_ActivePlayer->selected_hero_id > ID_NONE && o_ActivePlayer->selected_hero_id < 156) { _Hero_* hero = o_GameMgr->GetHero(o_ActivePlayer->selected_hero_id); int heroArmyPower = CALL_1(int, __fastcall, 0x44A950, &hero->army);
int defArmyPower = 20 * o_CreatureInfo[CID_DIAMOND_GOLEM].AI_value + 40 * o_CreatureInfo[CID_GOLD_GOLEM].AI_value;
if (defArmyPower) // На всякий (чтобы избежать неожиданного деления на 0) { double k = (double)heroArmyPower / defArmyPower;
// Вычисляем координаты входа int mapSize = o_AdvMgr->map->size; int pos = mapItem - o_AdvMgr->map->items; char x = pos % mapSize; pos /= mapSize; char y = pos % mapSize; char z = pos / mapSize;
// Получаем количество стеков, на которое разбилась охрана int stackCount = getStacksCount(k, x, y, z);
stackCount -= 2; if (stackCount < 1) stackCount = 1;
// Передаём адрес текстового буфера в качестве аргумента для диалога sprintf(o_TextBuffer, "%s\n\n%s: %d\n%s: %d\n\n# of stacks of %s: %d", (char*)c->ecx, o_CreatureInfo[CID_DIAMOND_GOLEM].name_plural, 20, o_CreatureInfo[CID_GOLD_GOLEM].name_plural, 40, o_CreatureInfo[CID_GOLD_GOLEM].name_plural, stackCount); c->ecx = (int)o_TextBuffer; } } }
mapItem->SetAsVisited(o_GameMgr->GetMeID());
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->object_type == 0x3F) { // Если есть охрана if (mapItem->setup & 1) { // Если герой выбран if (o_ActivePlayer->selected_hero_id > ID_NONE && o_ActivePlayer->selected_hero_id < 156) { _Hero_* hero = o_GameMgr->GetHero(o_ActivePlayer->selected_hero_id); int heroArmyPower = CALL_1(int, __fastcall, 0x44A950, &hero->army);
int defArmyPower = 20 * o_CreatureInfo[CID_DIAMOND_GOLEM].AI_value + 40 * o_CreatureInfo[CID_GOLD_GOLEM].AI_value;
if (defArmyPower) // На всякий (чтобы избежать неожиданного деления на 0) { double k = (double)heroArmyPower / defArmyPower;
// Вычисляем координаты входа int mapSize = o_AdvMgr->map->size; int pos = mapItem - o_AdvMgr->map->items; char x = pos % mapSize; pos /= mapSize; char y = pos % mapSize; char z = pos / mapSize;
// Получаем количество стеков, на которое разбилась охрана int stackCount = getStacksCount(k, x, y, z);
stackCount -= 2; if (stackCount < 1) stackCount = 1;
// Передаём адрес текстового буфера в качестве аргумента для диалога // sprintf(o_TextBuffer, "%s\n\n%s: %d\n%s: %d\n\n# of stacks of %s: %d", (char*)c->ecx, o_CreatureInfo[CID_DIAMOND_GOLEM].name_plural, 20, // o_CreatureInfo[CID_GOLD_GOLEM].name_plural, 40, o_CreatureInfo[CID_GOLD_GOLEM].name_plural, stackCount); // c->ecx = (int)o_TextBuffer;
string str = (char*)c->ecx; // Оригинальное сообщение str += "\n";
str = str + "\n" + to_string(stackCount); str = str + "\n" + to_string(k); sprintf(o_TextBuffer, "%s", str.c_str()); c->ecx = (int)o_TextBuffer; } } } }
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; }
// Заброшенная шахта if (mapItem->object_type == 0x35 && mapItem->os_type == 7) { // Если есть охрана if (*(char*)(o_GameMgr->Field<int>(0x4E38C) + (mapItem->setup << 6)) < 0) { // Если герой выбран if (o_ActivePlayer->selected_hero_id > ID_NONE && o_ActivePlayer->selected_hero_id < 156) { _Hero_* hero = o_GameMgr->GetHero(o_ActivePlayer->selected_hero_id); int heroArmyPower = CALL_1(int, __fastcall, 0x44A950, &hero->army);
_Army_* defArmy = (_Army_*)(o_GameMgr->Field<int>(0x4E38C) + (mapItem->setup << 6) + 4); int defArmyPower = CALL_1(int, __fastcall, 0x44A950, defArmy);
if (defArmyPower) // На всякий (чтобы избежать неожиданного деления на 0) { double k = (double)heroArmyPower / defArmyPower;
// Вычисляем координаты входа (можно через PosMixed, но пусть будет так) int mapSize = o_AdvMgr->map->size; int pos = ((int)mapItem - (int)o_AdvMgr->map->items) / sizeof(_MapItem_); char z = pos >= mapSize * mapSize; char x = pos % mapSize; char y = (pos - z * mapSize * mapSize) / mapSize;
// Получаем количество стеков, на которое разбилась охрана int stackCount = getStacksCount(k, x, y, z);
// Выясняем, есть ли улучшенный стек char* upgStr = upgradedStack(k, x, y, z) ? "Yes" : "No";
//********************** // Передаём адрес текстового буфера в качестве аргумента для диалога // sprintf(o_TextBuffer, "%s\n\n%s: %d\n\n%s%d\n\n%s%s", (char*)c->ecx, o_CreatureInfo[defArmy->type[0]].name_plural, defArmy->count[0], // "# of stacks: ", getStacksCount(k, x, y, z), "Upgraded stack: ", upgStr);
//********************** string str = (char*)c->ecx; // Оригинальное сообщение str += "\n"; int position = 0;
for (int i = 0; i < defArmy->count[0] % stackCount; ++i) { position++; if (upgStr == "Yes" && position == stackCount / 2 + 1) str = str + "\n" + o_CreatureInfo[CALL_1(int, __fastcall, 0x47AAD0, defArmy->type[0])].name_plural + ": "; else str = str + "\n" + o_CreatureInfo[defArmy->type[0]].name_plural + ": "; str = str + to_string(defArmy->count[0] / stackCount + 1); }
for (int i = 0; i < stackCount - (defArmy->count[0] % stackCount); ++i) { position++; if (upgStr == "Yes" && position == stackCount / 2 + 1) str = str + "\n" + o_CreatureInfo[CALL_1(int, __fastcall, 0x47AAD0, defArmy->type[0])].name_plural + ": "; else str = str + "\n" + o_CreatureInfo[defArmy->type[0]].name_plural + ": "; str = str + to_string(defArmy->count[0] / stackCount); }
// Передаём адрес текстового буфера в качестве аргумента для диалога sprintf(o_TextBuffer, "%s", str.c_str());
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(0x4A36E4, showAbandonedMine); // Abandoned Mine _PI->WriteLoHook(0x4A3EF5, showPyramid); // Pyramid _PI->WriteLoHook(0x4A435C, showRefugeeCamp); // Refugee Camp
_PI->WriteLoHook(0x413917, saveObjEntranceAddr); // Сохраняем адрес тайла-входа в объект _PI->WriteLoHook(0x4AC19D, Crypt_SkipMsgIfVisited); // убираем диалог обыска при посещении Склепа и Кораблей, когда они уже разграблены _PI->WriteLoHook(0x4A3ED3, Pyramid_SkipMsgIfVisited); // Пропускаем сообщения для Пирамиды, если она посещена } }
return TRUE; }
|