Версия для печати темы
DF2 :: ФОРУМЫ _ Моды _ Небольшие моды и плагины.
Автор: feanor 25 Jul 2012, 11:30
Эта тема предназначена для небольших модов, не требующих оформления отдельной темы.
Все обсуждения - в http://forum.df2.ru/index.php?showtopic=30850.
При выкладке указывайте:
- краткое описание. Скриншот - факультативно
- авторство (если не свое - особенно)
- формат (инсталлятор, архив или одиночный плагин),
- язык (или его отсутствие в случае безтекстового мода).
- разумеется, ссылку на скачивание. Не рекомендуются как zalil, rghost и подобные обменники с кратким сроком жизни файла, так и depositfiles, letitbit и прочие многоэтапные файлопохранилища.
Замена воговского таймера для ЭрыВсегда убивали воговские таймеры (которые TM, не TL).
Их мало (сто, из них изрядная часть зарезервирована), они перегружены и вообще мастдай.
В дллках оно еще более усугубляется - совершенно не хочется делать лишний хэндл для инициализации таймера, долбаться с его резервированием etc etc..
Поэтому для удобства написания дллок я набросал простенькую библиотеку, которая хукает RunTimer и посылает сообщение плагинам и ERM'у.
Требует Бараторчевский патчер, ибо он трушный.
https://dl.dropbox.com/u/61759222/HoMM/timerevent.era
https://dl.dropbox.com/u/61759222/HoMM/eratimer_code.rar
Интерфейс:
Простой как топор.
Для ерм: функция !?FU4074700, в x1 - номер игрока (0..7), в x2 - день (1,2,3,4,5,6,7,8..)
Для эры: событие OnGlobalTimer с двумя аналогичными параметрами. См. пример ниже.
Повторю еще раз,
требует patcher_x86.dll в корне папки с героями!!Пример (Си)
Код
void __stdcall TimerTest (PEvent e)
{
char buf[128];
int i = *(int*)(e->Data);
sprintf(buf,"IF:L^%i %i^;",*(int*)(e->Data), *(1+(int*)(e->Data)));
ExecErmCmd(buf);
}
..
RegisterHandler(TimerTest,"OnGlobalTimer");
Код:
<div class="sp-wrap"><div class="sp-body" title="Код">
Код
#include <windows.h>
#include <stdio.h>
#include "....includeera.h"
#include "....includeheroes.h"
#include "....includepatcher_x86_commented.hpp"
Patcher * globalPatcher;
PatcherInstance *patcher;
int __cdecl OnAnyTimer(HiHook* h, int owner)
{
ErmX[1]=owner;
ErmX[2]=CALL_0(int, __cdecl ,0x7103D2);
FireErmEvent(4074700);
int param[2] = {owner,CALL_0(int, __cdecl ,0x7103D2)};
FireEvent("OnGlobalTimer",(void*)param,8);
return EXEC_DEFAULT;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
globalPatcher = GetPatcher();
patcher = globalPatcher->CreateInstance("timerwrapper");
ConnectEra();
patcher->WriteHiHook(0x74DC74, SPLICE_, EXTENDED_, CDECL_, (void*)OnAnyTimer);
}
return TRUE;
}
</div></div>
Просьба здесь особо не обсуждать. Мне куда более доставляет складывать добро в чистую тему.
Автор: feanor 18 Aug 2012, 22:10
Обработка копания (granite.dll)
https://dl.dropbox.com/u/61759222/granite.era
https://dl.dropbox.com/u/61759222/granite.rar
Интерфейс: только для ERM.
События:
!?FU4074001 - происходит до раскопок (сразу после нажатия на "D")
x1 - номер героя
x2 - указатель на флаг отмены раскопок. !!UN:Cx2/4/0; для запрета, !!UN:Cx2/4/1; для разрешения (вариант по умолчанию)
!?FU4074003 - происходит при показе какого-либо сообщения в процессе раскопок.
x1 - номер героя
x2 - указатель на флаг отмены показа сообщения (действие типа появления ямы не отменится!). Работа с ним опять же, через UN:C
x3 - тип сообщения:
0 - "раскопки займут целый день. Приходите завтра."
1 - "инвентарь полон и раскопки будут бесполезны" (при собстна забитом инвентаре)
2 - "ищите Грааль на суше!" (при попытке выкопать яму в море)
3 - "ищите Грааль в чистом поле" (при поиске на каком-либо объекте)
4 - "поздравляем! вы нашли Грааль" (первое сообщение при успешной находке)
5 - "принесите Грааль в город.." (второе сообщение)
6 - "ничего нет. Куда подевалось?"
!?FU4074002 - происходит после раскопок
x1 - номер героя
x2 - результат. Аналогичен x3 для FU4074003, но c добавлением варианта -1 - "раскопки отменены в 4074001"
Пример (ERM)
Код
!?FU4074003;
!!if&x3=6:;
!!UN:Cx2/4/0;
!!IF:M^Держитесь подальше от торфяных болот!^;
!!HE-1:Tv998/v999/v1000/47/1;
!!el:;
!!en:;
Автор: feanor 18 Aug 2012, 22:41
Работа с датой (malachite.dll)
https://dl.dropbox.com/u/61759222/malachite.era
Дает возможность устанавливать внутригровую дату (экспортируемая функция SetDate(short day, short week, short month)), изменять анимацию нового дня/недели.
При загрузке анимации нового дня/недели запускается событие OnNewDayAnimation с первым параметром - указателем на строку с именем анимации и ERM-функция 4074218, которой передаются день/неделя/месяц в x1-x3 и имя анимации в z1.
Пример (ERM)
Код
!?FU4074218;
!!VRz1&x2=4/x3=6:S^midsumm.def^;
Пример (Си)
Код
void __stdcall Testt (PEvent e)
{
if (EventParams[1]==4 && EventParams[2]==6)
strcpy((char*)(e->Data),"midsumm.def");
}
RegisterHandler(Testt,"OnNewDayAnimation");
Код
// dllmain.cpp: определяет точку входа для приложения DLL.
#include <windows.h>
#include <stdio.h>
#include "..\..\include\era.h"
#include "..\..\include\heroes.h"
#include "..\..\include\patcher_x86_commented.hpp"
Patcher * globalPatcher;
PatcherInstance *patcher;
extern "C" __declspec(dllexport) void SetDate(short day, short week, short month)
{
*(short*)(0x1F63E+*(int*)0x699538) = day;
*(short*)(0x1F640+*(int*)0x699538) = week;
*(short*)(0x1F642+*(int*)0x699538) = month;
}
void* __fastcall BuildAndLoadNewDayDef(void* _this, int edx, int posX, int posY, int sizeX, int sizeY, int itemId, char *defname, int cadre, int group, int mirror, int closeDialog, int flags)
{
short day = *(short*)(0x1F63E+*(int*)0x699538);
short week = *(short*)(0x1F640+*(int*)0x699538);
short month = *(short*)(0x1F642+*(int*)0x699538);
strncpy(ErmZ[1],defname,512);
EventParams[0]=day;
EventParams[1]=week;
EventParams[2]=month;
FireEvent("OnNewDayAnimation",(void*)(ErmZ+1),4);
ErmX[1]=day;
ErmX[2]=week;
ErmX[3]=month;
FireErmEvent(4074218);
return CALL_12(void*, __thiscall, 0x4EA800,_this, posX, posY, sizeX, sizeY, itemId, ErmZ[1], cadre, group, mirror, closeDialog, flags);
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
//инит Эры, инит патчера
globalPatcher = GetPatcher();
patcher = globalPatcher->CreateInstance("malachite");
ConnectEra();
patcher->WriteHiHook(0x450CB7,CALL_,DIRECT_,THISCALL_,(void*)BuildAndLoadNewDayDef);
}
return TRUE;
}
Автор: feanor 19 Oct 2012, 20:35
Контроль восьмого слота (obsidian.dll)
Плагин: https://dl.dropbox.com/u/61759222/HoMM/obsidian.era
События:
!?FU4074810 (OnAdditionalMonsterAvailable) - происходит при каждой проверке на доступность восьмого слота.
x1 - номер замка
x2 - адрес его структуры
x3 - указатель на флаг наличия/отсутствия оного слота. Ну, как обычно.
!?FU4074811 (OnAdditionalMonsterSetting) - происходит после установки обитателей в жилище. В этом событии можно записать этих самых обитателей.
x1 - номер замка
x2 - адрес его структуры
x3 - указатель на тип монстров
x4 - указатель на количество монстров. Количество монстров записывается в два байта!!
(пояснение: при помощи последних двух полей можно установить "постояльцев", не прибегая к экспортируемым функциям)
Экспортируемые функции:
extern "C" __declspec(dllexport) void SetAdditionalMonster(int castle_id, int type, int amount) - устанавливает в замок castle_id монстров type числом amount
extern "C" __declspec(dllexport) int GetAdditionalMonsterType(int castle_id) - возвращает индекс монстра из восьмого слота замка castle_id
extern "C" __declspec(dllexport) int GetAdditionalMonsterAmount(int castle_id) - возвращает количество монстров из восьмого слота замка castle_id
Ну понятно по сигнатурам, да.
Пример (ERM)
Во всех Инферно можно нанимать 5 Монахов Баа каждую неделю.
Код
ZVSE
!?FU4074811;
!!CA0/x1:T?y10;
!!SN&y10=3:L^obsidian.era^/?y10 Ay10/^SetAdditionalMonster^/?y20 Ey20/1/x1/169/5;
!?FU4074810;
!!CA0/x1:T?y10;
!!UN&y10=3:Cx3/4/1;
!?PI;
!!MA:O169/3;
!!VRz47:S^Монах Баа^;
!!VRz48:S^Монахи Баа^;
!!UN:G1/169/0/47 G1/169/1/48;
Другой вариант первого триггера:
Код
!?FU4074811;
!!CA0/x1:T?y10;
!!UN&y10=3:Cx3/4/169;
!!UN&y10=3:Cx4/2/5;
Код
// dllmain.cpp: определяет точку входа для приложения DLL.
#include <windows.h>
#include <stdio.h>
#include "..\..\include\era.h"
#include "..\..\include\heroes.h"
#include "..\..\include\patcher_x86_commented.hpp"
Patcher * globalPatcher;
PatcherInstance *patcher;
extern "C" __declspec(dllexport) void SetAdditionalMonster(int castle_id, int type, int amount)
{
CASTLE *cstl = ((CASTLE*)(*(int*)((*(int*)0x699538) + 0x21614) + sizeof(CASTLE) * castle_id));
cstl->EightMonsterAmount = amount;
cstl->EightMonsterType = type;
}
extern "C" __declspec(dllexport) int GetAdditionalMonsterType(int castle_id)
{
CASTLE *cstl = ((CASTLE*)(*(int*)((*(int*)0x699538) + 0x21614) + sizeof(CASTLE) * castle_id));
return cstl->EightMonsterType;
}
extern "C" __declspec(dllexport) int GetAdditionalMonsterAmount(int castle_id)
{
CASTLE *cstl = ((CASTLE*)(*(int*)((*(int*)0x699538) + 0x21614) + sizeof(CASTLE) * castle_id));
return cstl->EightMonsterAmount;
}
int IsEighthSlotAvailable(CASTLE* cstl)
{
int ret = false;
if (cstl->Type == 5 && cstl->Built[2]&64)
ret = true;
EventParams[0]=cstl->Number;
EventParams[1]=(int)cstl;
EventParams[2]=(int)&ret;
FireEvent("OnAdditionalMonsterAvailable",0,0);
ErmX[1]=cstl->Number;
ErmX[2]=(int)cstl;
ErmX[3]=(int)&ret;
FireErmEvent(4074810);
return ret;
}
int __stdcall hook_5BDCBF(LoHook* h, HookContext* c)
{
CASTLE *cstl = (CASTLE*)c->ecx;
EventParams[0]=cstl->Number;
EventParams[1]=(int)cstl;
EventParams[2]=0x3C+(int)cstl;
EventParams[3]=0x40+(int)cstl;
FireEvent("OnAdditionalMonsterSetting",0,0);
ErmX[1]=cstl->Number;
ErmX[2]=(int)cstl;
ErmX[3]=0x3C+(int)cstl;
ErmX[4]=0x40+(int)cstl;
FireErmEvent(4074811);
return EXEC_DEFAULT;
}
int __stdcall hook_4C8B39(LoHook* h, HookContext* c)
{
CASTLE *cstl = (CASTLE*)c->ecx;
if (!IsEighthSlotAvailable(cstl))
c->return_address = 0x4C8B62;
else
c->return_address = 0x4C8B5D;
return NO_EXEC_DEFAULT;
}
int __stdcall hook_51D2AE(LoHook* h, HookContext* c)
{
CASTLE *cstl = (CASTLE*)c->esi;
if (!IsEighthSlotAvailable(cstl))
c->return_address = 0x51E79B;
else
c->return_address = 0x51D2DB;
return NO_EXEC_DEFAULT;
}
int __stdcall hook_5C651F(LoHook* h, HookContext* c)
{
CASTLE *cstl = (CASTLE*)c->esi;
if (!IsEighthSlotAvailable(cstl))
c->return_address = 0x5C66D9;
else
c->return_address = 0x5C654D;
return NO_EXEC_DEFAULT;
}
int __stdcall hook_5D8BD8(LoHook* h, HookContext* c)
{
CASTLE *cstl = (CASTLE*)c->ecx;
if (!IsEighthSlotAvailable(cstl))
c->return_address = 0x5D8C08;
else
c->return_address = 0x5D8BFE;
return NO_EXEC_DEFAULT;
}
int __stdcall hook_5D8C11(LoHook* h, HookContext* c)
{
CASTLE *cstl = (CASTLE*)c->edx;
if (!IsEighthSlotAvailable(cstl))
c->return_address = 0x5D8C95;
else
c->return_address = 0x5D8C37;
return NO_EXEC_DEFAULT;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
//инит Эры, инит патчера
globalPatcher = GetPatcher();
patcher = globalPatcher->CreateInstance("obsidian");
ConnectEra();
patcher->WriteLoHook(0x4C8B39,(void*)hook_4C8B39); //JNE SHORT 004C8B62
patcher->WriteLoHook(0x51D2AE,(void*)hook_51D2AE); //JNE 0051E79B
patcher->WriteLoHook(0x5C651F,(void*)hook_5C651F); //JNE 005C66D9
patcher->WriteLoHook(0x5D8BD8,(void*)hook_5D8BD8); //JNE SHORT 005D8C08
patcher->WriteLoHook(0x5D8C11,(void*)hook_5D8C11); //JNE SHORT 005D8C08
patcher->WriteLoHook(0x5BDCBB,(void*)hook_5BDCBF); //после записи в новый день
//RegisterHandler(TimerTest,"OnGlobalTimer");
}
return TRUE;
}
Автор: feanor 03 Jan 2013, 00:10
Отрицательная удача (badluck.dll) (с) Sav, feanor
Плагин (SoD, Era): https://dl.dropbox.com/u/61759222/HoMM/badluck.era, http://master-cadr.ru/HMM/Plugins/EraPlugins.zip
Чуть-чуть информации о функционировании удачи вообще:
"удачливость" стека определяется во время атаки (собстна, немногим ранее проигрывания анимации) и записывается в структуру стека, в поле со смещением +0x70 (1 - удача, 0 - обычная атака, добавляемое плагином -1 - неудача).
Проверять через ERM, соответственно, можно пресловутой командой !!BM:G, подсунув ей в качестве номера заклинания индекс -74.
Потом, при нанесении ущерба это поле проверяется и уже подсчитывается собственно ущерб.
Автор: feanor 08 Feb 2013, 15:55
Настраиваемые имена классов (turquoise.dll)
Плагин: https://dl.dropbox.com/u/61759222/HoMM/turquoise.zip, http://master-cadr.ru/HMM/Plugins/turquoise.zip
Позволяет настраивать индивидуальное имя класса для каждого героя (аналогично Джем из стандартных кампаний SoD), как с помощью конфигурационного файла (приложен в архиве), так и динамически, с помощью SN:W-переменных с именем hero_class_name_<hero_id>.
Иллюстрация:
Код
Patcher * globalPatcher;
PatcherInstance *patcher;
_ptr_ GetClassNameDefault;
char* GetClassName(HERO* hero)
{
char tmp[64];
sprintf(tmp,"SN:W^hero_class_name_%i^/?z1;", hero->Number);
ExecErmCmd(tmp);
if(ErmZ[1][0]!=0)
{
return ErmZ[1];
}
return CALL_1(char*, __thiscall, GetClassNameDefault, hero);
}
void __stdcall InitNames(PEvent e)
{
char tmp[1024];
char tmp_erm[1024];
for(int i=0; i!=256;i++)
{
sprintf(tmp,"Hero%i",i);
ReadStrFromIni(tmp, "Classnames", "turquoise.ini", (char*)tmp);
if (*tmp)
{
sprintf(tmp_erm,"SN:W^hero_class_name_%i^/^%s^;", i, tmp);
ExecErmCmd(tmp_erm);
}
}
}
char* __stdcall GetClassName_hook(HiHook* h, HERO* hero)
{
return GetClassName(hero);
}
int __stdcall hook_4E1(LoHook* h, HookContext* c)
{
c->ecx = (int)GetClassName((HERO*)(c->eax));
return EXEC_DEFAULT;
}
int __stdcall hook_4DB(LoHook* h, HookContext* c)
{
c->eax = (int)GetClassName((HERO*)(c->ecx));
return EXEC_DEFAULT;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
//инит Эры, инит патчера
globalPatcher = GetPatcher();
patcher = globalPatcher->CreateInstance(PINSTANCE_MAIN);
ConnectEra();
RegisterHandler(InitNames, "OnBeforeErmInstructions");
RegisterHandler(InitNames, "OnAfterCreateWindow");
HiHook *h = patcher->WriteHiHook(0x4D91E0,SPLICE_,EXTENDED_,THISCALL_,(void*)GetClassName_hook);
GetClassNameDefault = h->GetDefaultFunc();
patcher->WriteLoHook(0x4E1DE6, (void*)hook_4E1);
patcher->WriteLoHook(0x4DB980, (void*)hook_4DB);
patcher->WriteLoHook(0x4DBDF7, (void*)hook_4DB);
}
return TRUE;
}
Автор: feanor 28 Mar 2013, 14:27
Вызовы элементалей (summoniots.dll)
Плагин: https://dl.dropbox.com/u/61759222/HoMM/summoniots.dll
Позволяет подменять результаты заклинаний вызова элементалей.
Помимо этого, отключает невозможность применения более чем одного заклинания вызова.
События: !?FU4074520 - попытка вызова.
x1 - указатель на флаг того, сработает ли заклинание (не сработавшее заклинание тратит ману и магический ход, но ничего не делает)
x2 - указатель на номер заклинания
x3 - указатель на номер существа
x4 - указатель на уровень колдовской силы, используемый при расчете количества вызванных.
Пример (ERM):
Вызов чародеев вместо любого из элементалей.
Код
ZVSE
!?FU4074520;
!!UN:Cx3/4/?y1; --кого вызываем
!!BG:H?y3;
!!IF:L^Hero %Y3 trying to summon %Y1..^;
!!UN:Cx3/4/136;
!!UN:Cx4/4/999;
!!UN:Cx3/4/?y1;
!!IF:L^..but summons horde of %Y1^;
Код
Patcher * globalPatcher;
PatcherInstance *citrine;
int __stdcall SummonHook(HiHook* h, _BattleMgr_ *combatman, int spell, int creature, int spellpower, int unk)
{
int flag = 1;
((int*)0x91DA34)[1] = (int)(&flag);
((int*)0x91DA34)[2] = (int)(&spell);
((int*)0x91DA34)[3] = (int)(&creature);
((int*)0x91DA34)[4] = (int)(&spellpower);
((int*)0x91DA34)[5] = (int)(&unk);
((int*)0x91DA34)[6] = (int)combatman+combatman->current_side*4+0x132C0;
CallERM(4074520);
return flag?CALL_5(int, __thiscall, h->GetDefaultFunc(), (int)combatman, spell, creature, spellpower, unk):-1;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
globalPatcher = GetPatcher();
citrine = globalPatcher->CreateInstance("summon_elems");
citrine->WriteHiHook(0x5A7390, SPLICE_, EXTENDED_, THISCALL_, (void*)SummonHook);
citrine->WriteByte(0x59F887,0xEB);
citrine->WriteWord(0x5A96D0, 0x9090);
}
return TRUE;
}
Автор: feanor 17 Jul 2013, 18:31
Гильдия воров вместо "Братства меча" в Замке.
Надстройка над таверной теперь исполняет функции представительства Гильдии Воров: при её постройке соответствующее меню в таверне будет показывать полную информацию о противниках и союзниках. Цена строения - прежняя, 500 золота и 5 дерева.
Автор: feanor
Язык: русский (изменена одна строчка в bldgspec.txt)
Формат: zip-архив с папкой мода.
https://dl.dropboxusercontent.com/u/61759222/HoMM/thiefguild.zip
Автор: Iv 18 Jul 2013, 13:31
Другие флаги игроков
Автор: Iv (с участием Algor)
Язык: нет
Формат: pac-файл для Era 2.*. Кинуть в папку любого мода
http://sites.google.com/site/hommfun/files/alternative%20flags.pac (390 кб)
Автор: feanor 25 Jul 2013, 08:00
Несколько небольших патчей.
Автор: feanor
Язык: нет
Формат: bin-файлы. Установка через копирование в eraplugins/afterwog любого мода.
Нет подземелий в подземельях.
Тип поля боя на подземном уровне будет определяться типом почвы. В оригинале все бои в подземке проходили на подземной почве.
https://dl.dropboxusercontent.com/u/61759222/HoMM/no_underground_at_underground.bin
Исправление вылета при проигрыше битвы в ERM-событии
При проигрыше битвы, вызванной в триггере !?HM, игра вылетала. Теперь - нет.
https://dl.dropboxusercontent.com/u/61759222/HoMM/fix_encounter_crash.bin
Отключение исключений при старте карты
При рестарте карты OllyDbg отвлекается на какое-то исключение, что вынуждает переключаться между игрой и отладчиком. Теперь - нет.
Плагин нужен лишь при анализе игры! Не уверен в отсутствии побочных эффектов.
https://dl.dropboxusercontent.com/u/61759222/HoMM/no_raise_exception_at_map_loading.bin
Автор: Iv 25 Jul 2013, 16:02
http://yadi.sk/d/XoIiMYNU7D1Av (39 б)
Автор: Master of puppets
Язык: нет
Формат: bin-арник для Эры. Кинуть в папку eraplugins/afterwog
Описание: Стракер поднимает не скелетов, а зомби
Автор: feanor 01 Aug 2013, 14:30
Часы реального времени.
ПКМ по дате на экране карты выведет реальное время и заряд батареи (при наличии таковой). Для играющих в полноэкранном режиме.
Автор: feanor
Язык: английский
Формат: erm-скрипт. Установка через копирование в data/s любого мода.
https://dl.dropboxusercontent.com/u/61759222/HoMM/clock.erm
Автор: feanor 16 Sep 2013, 00:59
Еженедельная газета "Голос из-под стойки".
В начале каждой недели в чат выводятся текущие слухи и имена героев-наемников.
Автор: feanor
Язык: русский.
Формат: zip-архив с папкой мода.
https://dl.dropboxusercontent.com/u/61759222/HoMM/daily_event.zip
Автор: Iv 19 Sep 2013, 23:55
-Why do you call your software "beta"?
-'coz it's beta than nothing.
Замена вызовов, версия 0.3
Вызовы элементалей заменяются на вызовы других существ
В мод включена измененная раскладка заклинаний.
Автор: Iv (с участием Algor)
Язык: русский
Формат: архив с папкой мода для Эры 2.х
https://dl.dropboxusercontent.com/u/58527032/HMM/H3IVsummon.exe
Автор: igrik 21 Sep 2013, 23:13
Рюкзак героя
Версия 1.04.
Автор: igrik
Язык: русский и английский
Формат: архив с папкой мода для ERA II
Спасибо Berserker, baratoch.
Функция аналогичная функции в HDmod, но с меньшим функционалом (не реализована и не планируется передача артов по Ctrl, Shift, Alt)
Совместима с большинством модов (за исключением Феникс-Мода! ввиду пересечения расположения кнопок)
Поддерживает Hot Seat, TCP/IP, новые артефакты от feanor'a "emerald.dll v2.01" и т.п.
Спасибо: Berserker, baratorch.
http://yadi.sk/d/D3YfCX8h9riKu
http://yadi.sk/d/HtXe4r4hCVTCs
*********
Исправлено:
[+] Нормальное отображение описания артефактов! За что огромное спасобо Berserker'у!
[-] Больше не используется файл "artifact.def", который мог перекрывать аналогичный из других модов.
[-] Исправлен баг, при котором игра вылетала если в рюкзаке лежала книга заклинаний.
[-] Исправлен баг вследствие некорректного действия команды "HE:A1".
[+] Улучшена подложка
[+] Подсказка при наведении мыши на элемент диалога. (для временного хранения используются переменные z1 - z65)
Автор: Iv 13 Mar 2014, 13:14
Случайные нейтральные герои
Нейтралы с вероятностью 10/20/30/40/50% в зависимости от уровня сложности получают героя-предводителя,
который будет сражаться на их стороне в бою против игрока-человека.
Автор:
Algor,
IvФормат: sfx-архив с папкой мода.
http://yadi.sk/d/-RKI2enLJkBLu (306 Кб)
Автор: feanor 22 Apr 2014, 20:39
Статуя Ленина в Инферно вместо Бога Огня
Автор: feanor
Формат: архив с папкой мода.
https://dl.dropboxusercontent.com/u/61759222/HoMM/lenin.zip
Автор: Iv 15 Jun 2014, 20:37
https://cloud.mail.ru/public/8d9b98a1c7af/Dirt_for_dungeon.bin
Маленький патчик для рандомок без подземного уровня. На них Подземелье генерится на грязи, но бонуса родной земли не получает. Патчик исправляет эту несправедливость.
Автор: Iv
Формат: bin-файл для Era II. Класть в \EraPlugins\AfterWoG\
Автор: Orzie 15 Jul 2014, 13:12
Культ Вождя Мирового Пролетариата
Авторы: feanor, Orzie, Docent Picolan, Bes
Формат: SFX-архив мода для ERA II, спрашивающий директорию установленной игры.
Апгрейд мода "http://forum.df2.ru/index.php?showtopic=30848&view=findpost&p=649101"
Определённые строения в Инферно, Некрополисе и Замке будут содержать различные образы вождя пролетариата - В.И.Ленина.
https://app.box.com/s/qmfj3km81roz85tutojf
Автор: feanor 31 Jul 2014, 22:57
Магические сундуки
Сундуки с артефактами теперь отличаются по виду от сундуков с золотом.
Автор: Axolotl, feanor
Язык: нет
Формат: zip-архив с папкой мода.
https://dl.dropboxusercontent.com/u/61759222/HoMM/magechest.zip
Автор: feanor 03 Feb 2015, 19:06
Вуду-гарпии.
Иногда гарпии-ведьмы будут атаковать с места, не приближаясь к противнику. Анимация этой атаки будет отличаться от обычной.
Автор: feanor
Язык: нет
Формат: erm-скрипт. Установка через копирование в data/s любого мода.
https://dl.dropboxusercontent.com/u/61759222/HoMM/voodoo.erm
Автор: feanor 10 Feb 2015, 03:13
Иной формат даты.
Дни недели и месяцы именуются, а не нумеруются.
Автор: feanor
Язык: русский
Формат: dll-плагин. Установка через копирование в /eraplugins любого мода.
https://dl.dropboxusercontent.com/u/61759222/HoMM/human_month_names.dll
Автор: hippocamus 12 Feb 2015, 16:27
Приручённые нейтралы
Большинство нейтралов теперь принадлежат фракциям, к которым они сродны.
Орки на кабанах - Цитадель
Мумии - Некрополис
Тролли - Крепость
Крестьне - Замок
Воры - Темница
Хоббиты - Башня
Внефракционными остались высшие драконы, чародеи, снайперы и кочевники (есть соблазн приписать их к Сопряжению - по антуражу подходят, но стоит ли?)
автор: hippocamus
формат: bin для SOD, HotA + HD (возможно Era)
язык: отсутствует
https://dl.dropboxusercontent.com/u/56557111/domest.bin (76 байт)
Кампанейские герои доступны как стартовые
На случайных картах можно стартовать любым героем
автор: возможно, МоП (я взял из ExeBuilder и перебил адреса, адаптировал под ХД)
формат: bin для SoD+HD (под Хоту не работает)
язык: отсутствует
https://dl.dropboxusercontent.com/u/56557111/campaign%20heroes%20as%20start%20heroes%20%28modified%29.bin (301 байт)
Автор: Iv 19 Feb 2015, 23:46
https://dl.dropboxusercontent.com/u/58527032/HMM/2well.bin для бога колодцев
Маленький патчик для рандомок. На карте генерится больше колодцев. Иногда даже слишком много
Автор: Iv (инфа от Sav)
Формат: bin-файл для Era II. Класть в \EraPlugins\AfterWoG\ любого активного мода
Автор: feanor 13 Mar 2015, 23:57
В честь минувших праздников полового диморфизма..
Зависящие от пола имена классов.
Автор: feanor
Язык: русский
Формат: папка с модом.
https://dl.dropboxusercontent.com/u/61759222/HoMM/gcn.zip
Автор: feanor 14 Aug 2016, 00:56
Анимированные существа на поле боя (порт из WoG 3.59)
Автор: sergroj
Портировано: feanor в рамках проекта TSW; разрешение автора получено.
Формат: папка с модом.
https://dl.dropboxusercontent.com/u/61759222/HoMM/InstantAnimation.zip
Скорости анимаций настраиваются индивидуально для каждого типа существ в файле anims.ini; существующие параметры взяты из блока monanim0 хотовского HotA.dat (версия 1.3.4), для вог-монстров взято по подобию.
Помимо этого, есть глобальный множитель, позволяющий ускорить или замедлить анимацию для всех монстров без пересчета всех параметров.
Автор: igrik 21 Oct 2016, 19:17
Цифровое отображение морали и удачи в окне стека.
Как в MOP
автор: igrik
язык: нет
формат: dll (плагин для HD-мода)
версии: SoD/WoG/ERA
https://dl.dropboxusercontent.com/s/m3wu5myx5ht0xxk/MonDecsription.dll (8.50kb)
Код
/////////////////////////////////////////////////////////
// Расширение диалога монстров: //
// - отображение единиц удачи и морали (igrik) //
// - отображение длительности заклинаний (из HotA) //
/////////////////////////////////////////////////////////
#include "..\..\include\homm3.h"
// Объекты patcher_x86.
Patcher* _P;
PatcherInstance* _PI;
int __stdcall Y_MoralLuckBonus(LoHook* h, HookContext* c)
{
// цифровое отображение морали
if (*(int*)(c->edi+1256) < 1)
sprintf(o_TextBuffer, "%d", *(int*)(c->edi+1256));
else
sprintf(o_TextBuffer, "+%d", *(int*)(c->edi+1256));
int bonMoralNew = CALL_1 (int, __cdecl, 6386834, 80);
int bonMoral = CALL_12 (int, __thiscall, 6014624, bonMoralNew, 48, 209, 20, 20, o_TextBuffer, "tiny.fnt", 4, 3006, 10, 0, 8);
CALL_4 (int, __thiscall, 0x5FE2D0, c->ebx, *(int*)(c->ebx+8), 1, &bonMoral);
// цифровое отображение удачи
if (*(int*)(c->edi+1260) < 1)
sprintf(o_TextBuffer, "%d", *(int*)(c->edi+1260));
else
sprintf(o_TextBuffer, "+%d", *(int*)(c->edi+1260));
int bonLuckNew = CALL_1 (int, __cdecl, 6386834, 80);
int bonLuck = CALL_12 (int, __thiscall, 6014624, bonLuckNew, 101, 209, 20, 20, o_TextBuffer, "tiny.fnt", 4, 3007, 10, 0, 8);
CALL_4 (int, __thiscall, 0x5FE2D0, c->ebx, *(int*)(c->ebx+8), 1, &bonLuck);
return EXEC_DEFAULT;
}
int __stdcall Y_MoralLuckBonus2(LoHook* h, HookContext* c)
{
if (*(int*)(c->ebx+104) < 1)
sprintf(o_TextBuffer, "%d", *(int*)(c->ebx+104));
else
sprintf(o_TextBuffer, "+%d", *(int*)(c->ebx+104));
int bonMoralNew = CALL_1 (int, __cdecl, 6386834, 80);
int bonMoral = CALL_12 (int, __thiscall, 6014624, bonMoralNew, 48, 209, 20, 20, o_TextBuffer, "tiny.fnt", 4, 3006, 10, 0, 8);
CALL_4 (int, __thiscall, 0x5FE2D0, c->ebx+48, *(int*)(c->ebx+56), 1, &bonMoral);
// отображение единиц удачи
if (*(int*)(c->ebx+124) < 1)
sprintf(o_TextBuffer, "%d", *(int*)(c->ebx+124));
else
sprintf(o_TextBuffer, "+%d", *(int*)(c->ebx+124));
int bonLuckNew = CALL_1 (int, __cdecl, 6386834, 80);
int bonLuck = CALL_12 (int, __thiscall, 6014624, bonLuckNew, 101, 209, 20, 20, o_TextBuffer, "tiny.fnt", 4, 3007, 10, 0, 8);
CALL_4 (int, __thiscall, 0x5FE2D0, c->ebx+48, *(int*)(c->ebx+56), 1, &bonLuck);
return EXEC_DEFAULT;
}
int __stdcall Y_SpellShow(LoHook* h, HookContext* c)
{
// цифровое отображение длительности заклинаний (вытащено из HotA.dll)
int Spell = *(int*)(c->esi);
int DlgShow = *(int*)(c->ebp-40);
if (Spell >=0)
{
if (Spell == 47 || Spell == 59 || Spell == 72)
int Spell = 47;
else
{
sprintf(o_TextBuffer, "x%d", *(int*)(c->esi+12));
int SpellNew = CALL_1 (int, __cdecl, 6386834, 80);
int SpellShow = CALL_12 (int, __thiscall, 6014624, SpellNew, *(int*)(c->ebp-28), 202, 46, 20, o_TextBuffer, "tiny.fnt", 4, 3003 - *(int*)(c->ebp-32), 10, 0, 8);
CALL_4 (int, __thiscall, 0x5FE2D0, DlgShow + 48, *(int*)(DlgShow +56), 1, &SpellShow);
}
}
return EXEC_DEFAULT;
}
int __stdcall Y_SpellInfo1(LoHook* h, HookContext* c)
{
// в этой функции я не смог поставить HiHook
// потому что в WoGе после её выполнения вылетает
if( c->eax >= 3000 && c->eax <= 3007 )
{
switch (c->eax){
case 3000: c->eax = 11; break; // спелл_1
case 3001: c->eax = 11; break; // спелл_2
case 3002: c->eax = 11; break; // спелл_3
case 3006: c->eax = 9; break; // мораль
case 3007: c->eax = 10; break; // удача
default: c->eax = -1; break; // на всякий
}
c->return_address = 0x5F4B34;
return NO_EXEC_DEFAULT;
}
return EXEC_DEFAULT;
}
int __stdcall Y_SpellInfo2(LoHook* h, HookContext* c)
{
// подсказка для циферных отображений спеллов, морали и удачи
switch (c->esi){
case 3000: c->esi = 221; break; // спелл_1
case 3001: c->esi = 222; break; // спелл_2
case 3002: c->esi = 223; break; // спелл_3
case 3006: c->esi = 219; break; // мораль
case 3007: c->esi = 220; break; // удача
}
return EXEC_DEFAULT;
}
int __stdcall Y_SpellShow1(LoHook* h, HookContext* c)
{
int New = CALL_1 (int, __cdecl, 0x617492, 80);
int SpellShow = CALL_12 (int, __thiscall, 6014624, New, 15, c->ebx + 16, 46, 20, "", "tiny.fnt", 4, c->edi + 3003, 10, 0, 8);
CALL_4 (int, __thiscall, 0x4230D0, c->esi, *(int*)(c->esi + 8), 1, &SpellShow);
return EXEC_DEFAULT;
}
int __stdcall Y_SpellShow2(LoHook* h, HookContext* c)
{
int New = CALL_1 (int, __cdecl, 0x617492, 80);
int SpellShow = CALL_12 (int, __thiscall, 6014624, New, 15, *(int*)(c->ebp + 24) + 16, 46, 20, "", "tiny.fnt", 4, c->ebx + 3003, 10, 0, 8);
CALL_4 (int, __thiscall, 0x4230D0, c->esi, *(int*)(c->esi + 8), 1, &SpellShow);
return EXEC_DEFAULT;
}
int __stdcall Y_SpellShow3(LoHook* h, HookContext* c)
{
_Dlg_* dlg = (_Dlg_*)(c->edi + 56);
int item = 3006 - *(int*)(c->ebp + 8);
int spell = *(int*)c->esp - 1;
if (spell >= 0 && spell != 47 && spell != 59 && spell != 72)
sprintf(o_TextBuffer, "x%d", *(int*)(c->esi + 4 * spell + 408));
else
sprintf(o_TextBuffer, "");
((_DlgStaticText_*)dlg->GetItem(item))->SetText(o_TextBuffer);
return EXEC_DEFAULT;
}
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
static _bool_ plugin_On = 0;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
if (!plugin_On)
{
plugin_On = 1;
_P = GetPatcher();
_PI = _P->CreateInstance("MonDescription");
// Патч на tiny.fnt малые описания монстров (все в DEC)
*(int*)6243434 = 6687924; // в бою
*(int*)6244485 = 6687924; // при покупке
*(int*)6241855 = 6687924; // не в бою
_PI->WriteLoHook(0x5F3C43, Y_MoralLuckBonus); // мораль и удача в бою
_PI->WriteLoHook(0x5F439B, Y_MoralLuckBonus2); // мораль и удача вне в боя
// отображение длительности заклинаний в окне монстра (вытащено из HotA.dll)
_PI->WriteLoHook(0x5F6BE2, Y_SpellShow);
_PI->WriteLoHook(0x5F4B05, Y_SpellInfo1); // тут WoG/ERA не дают поставить HiHook
_PI->WriteLoHook(0x5F522A, Y_SpellInfo2);
// отображение длительности заклинаний в расширенной статистике (вытащено из HotA.dll)
_PI->WriteLoHook(0x46D12B, Y_SpellShow1);
_PI->WriteLoHook(0x46D4CB, Y_SpellShow2);
_PI->WriteLoHook(0x46D9F0, Y_SpellShow3);
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
Автор: Ben 08 Aug 2017, 21:11
Новая версия заклинания Городской портал
Авторы: AlexSpl, Ben
Dll плагин к HD моду.
Теперь на любом уровне магии Земли можно выбирать город. Существенно увеличились расходы MP - для основного уровня 1200 MP, для продвинутого 1000 MP, для эксперта 800 MP.
AI в курсе введенных изменений, применяет Городской портал для перемещения в нужный город, даже не имея навыка Магия Земли.
Код
#include "..\..\include\homm3.h"
// объекты patcher_x86.
Patcher* _P;
PatcherInstance* _PI;
int __stdcall tpCostForHuman(LoHook* h, HookContext* c)
{
*(int*)(c->ebp - 0x40) = 1200;
*(int*)(c->ebp - 0x3C) = 1200;
*(int*)(c->ebp - 0x38) = 1000;
*(int*)(c->ebp - 0x34) = 800;
return EXEC_DEFAULT;
}
int __stdcall skipTownPortalConfirm(LoHook* h, HookContext* c)
{
if ( c->eax == -1 ) {
c->ecx = *(unsigned char*)(c->edi + 5); // Выполняем затёртую jmp-патчем команду
c->return_address = 0x41D990; // Обходим jmp-патч
return NO_EXEC_DEFAULT;
}
c->return_address = 0x41D939;
return NO_EXEC_DEFAULT;
}
int __stdcall mpointsEarth_1(LoHook* h, HookContext* c) {
int level = c->eax;
int mpoints[] = {1200, 1200, 1000, 800};
c->eax = mpoints[level];
c->return_address = 0x56B5AA;
return NO_EXEC_DEFAULT;
}
int __stdcall mpointsEarth_2(LoHook* h, HookContext* c) {
int level = c->eax;
int mpoints[] = {1200, 1200, 1000, 800};
c->eax = mpoints[level];
c->ecx = *(int*)(c->esi + 0x4D);
c->return_address = 0x430532;
return NO_EXEC_DEFAULT;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
static _bool_ plugin_On = 0;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
if (!plugin_On)
{
plugin_On = 1;
_P = GetPatcher();
_PI = _P->CreateInstance("HD.Plugin.TownPortal");
_PI->WriteLoHook(0x41D538, tpCostForHuman); // меняем расходы MP c 300/300/200 на 1200/1000/800
_PI->WriteHexPatch(0x41D6D1, "90 90 90 90 90 90 90 90 90 90"); // отменяем проверку на уровень Магии Земли в главной функции заклинания
_PI->WriteLoHook(0x56B59B, mpointsEarth_1); // расходы MP во вспомогат. функции 1
_PI->WriteHexPatch(0x56B3B4, "90 90 90 90 90 90"); // отменяем проверку на уровень Магии Земли во вспомогат. функции 1
_PI->WriteLoHook(0x430520, mpointsEarth_2); // расходы MP во вспомогат. функции 2
_PI->WriteLoHook(0x41D934, skipTownPortalConfirm); // обходим jmp патч HD мода (версии 3.809 и выше)
}
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
Ссылка на DLL:
https://yadi.sk/d/BMehvHpF3MuoqR
Автор: igrik 09 Aug 2017, 17:47
А где сама DLL?
Кстати забыл. Тестировал. Багов не обнаружил, поэтому включил и в мультиплеере.
Автоулучшение существ в городе.
автор: [igrik]
язык: нет
формат: dll (плагин для HD-мода)
версии: SoD/HotA/ERA
https://dl.dropboxusercontent.com/s/cce2dn3jif22wof/HD_Plugin.AutoGradeMonInTown.dll (10.5kb)
Плагин позволяет улучшать существ в городе по комбинации клавиш [ЛКМ+A] (левая кнопка мыши + "A" в английской раскладке или "Ф" в русской раскладке):
- [ЛКМ+A] на иконке героя (или флаге): улучшение всех существ у данного героя/города. Исключения: Скелеты в Скелетов-Воинов, Корсары в Морских Волков (для HotA), специалисты по улучшению существ (например Джелу);
- [ЛКМ+A] на стеке: улучшение выбранного стека. Исключения: Корсары в Морских Волков (для HotA), специалисты по улучшению существ (например Джелу);
Поддерживаемые версии HoMM3: SoD, HotA, ERA/WoG.
Способ установки: это плагин для HD мода и его необходимо скопировать в "..[Ваша папка героев]\_HD3_Data\Packs\[Любое название]\.." и подключить это "Любое название" в лаунчере HD мода.
Код
//////////////////////////////////////////////////////////
// Плагин автоматического улучшения существ в городе //
// Автор: [igrik] //
/////////////////////////////////////////////////////////
#include "..\..\include\homm3.h"
// Объекты patcher_x86.
Patcher* _P;
PatcherInstance* _PI;
// улучшение всех стеков у героя или в городе
_bool_ Y_AutoGradeMonInTownFunc(_Town_* town, char type){
_Player_* me = o_GameMgr->GetMe();
int cost_res[7];
if (!me->IsActive()) return false;
if (type != 0 ) // если не город
{
_Hero_* hero = 0;
if (type == 1)
hero = o_GameMgr->GetHero(town->up_hero_id);
else hero = o_GameMgr->GetHero(town->down_hero_id);
if (me->id != hero->owner_id) return false;
for (char i = 0; i < 7; i++ )
{
if( hero->army.type[i] >= 0 )
{
if ( GetCreatureGrade(hero->army.type[i]) != -1 && hero->army.type[i] != 158 && hero->army.type[i] != 56)
{
if( town->IsBuildingBuilt(37 + o_pCreatureInfo[GetCreatureGrade(hero->army.type[i])].level, 1) && town->type == o_pCreatureInfo[hero->army.type[i]].town )
{
cost_res[0] = (o_pCreatureInfo[GetCreatureGrade(hero->army.type[i])].cost.wood * hero->army.count[i]) - (o_pCreatureInfo[hero->army.type[i]].cost.wood * hero->army.count[i]);
cost_res[1] = (o_pCreatureInfo[GetCreatureGrade(hero->army.type[i])].cost.mercury * hero->army.count[i]) - (o_pCreatureInfo[hero->army.type[i]].cost.mercury * hero->army.count[i]);
cost_res[2] = (o_pCreatureInfo[GetCreatureGrade(hero->army.type[i])].cost.ore * hero->army.count[i]) - (o_pCreatureInfo[hero->army.type[i]].cost.ore * hero->army.count[i]);
cost_res[3] = (o_pCreatureInfo[GetCreatureGrade(hero->army.type[i])].cost.sulfur * hero->army.count[i]) - (o_pCreatureInfo[hero->army.type[i]].cost.sulfur * hero->army.count[i]);
cost_res[4] = (o_pCreatureInfo[GetCreatureGrade(hero->army.type[i])].cost.crystal * hero->army.count[i]) - (o_pCreatureInfo[hero->army.type[i]].cost.crystal * hero->army.count[i]);
cost_res[5] = (o_pCreatureInfo[GetCreatureGrade(hero->army.type[i])].cost.jems * hero->army.count[i]) - (o_pCreatureInfo[hero->army.type[i]].cost.jems * hero->army.count[i]);
cost_res[6] = (o_pCreatureInfo[GetCreatureGrade(hero->army.type[i])].cost.gold * hero->army.count[i]) - (o_pCreatureInfo[hero->army.type[i]].cost.gold * hero->army.count[i]);
if (me->resourses.wood >= cost_res[0] && me->resourses.mercury >= cost_res[1] && me->resourses.ore >= cost_res[2] && me->resourses.sulfur >= cost_res[3] && me->resourses.crystal >= cost_res[4] && me->resourses.jems >= cost_res[5] && me->resourses.gold >= cost_res[6])
{
me->resourses.wood -= cost_res[0];
me->resourses.mercury -= cost_res[1];
me->resourses.ore -= cost_res[2];
me->resourses.sulfur -= cost_res[3];
me->resourses.crystal -= cost_res[4];
me->resourses.jems -= cost_res[5];
me->resourses.gold -= cost_res[6];
hero->army.type[i] = GetCreatureGrade(hero->army.type[i]);
}
}
}
}
}
return true;
}
else
{
if (me->id != town->owner_id) return false;
for (char i = 0; i < 7; i++ )
{
if( town->guards.type[i] >= 0 )
{
if ( GetCreatureGrade(town->guards.type[i]) != -1 && town->guards.type[i] != 158 && town->guards.type[i] != 56)
{
if( town->IsBuildingBuilt(37 + o_pCreatureInfo[GetCreatureGrade(town->guards.type[i])].level, 1) && town->type == o_pCreatureInfo[town->guards.type[i]].town )
{
cost_res[0] = (o_pCreatureInfo[GetCreatureGrade(town->guards.type[i])].cost.wood * town->guards.count[i]) - (o_pCreatureInfo[town->guards.type[i]].cost.wood * town->guards.count[i]);
cost_res[1] = (o_pCreatureInfo[GetCreatureGrade(town->guards.type[i])].cost.mercury * town->guards.count[i]) - (o_pCreatureInfo[town->guards.type[i]].cost.mercury * town->guards.count[i]);
cost_res[2] = (o_pCreatureInfo[GetCreatureGrade(town->guards.type[i])].cost.ore * town->guards.count[i]) - (o_pCreatureInfo[town->guards.type[i]].cost.ore * town->guards.count[i]);
cost_res[3] = (o_pCreatureInfo[GetCreatureGrade(town->guards.type[i])].cost.sulfur * town->guards.count[i]) - (o_pCreatureInfo[town->guards.type[i]].cost.sulfur * town->guards.count[i]);
cost_res[4] = (o_pCreatureInfo[GetCreatureGrade(town->guards.type[i])].cost.crystal * town->guards.count[i]) - (o_pCreatureInfo[town->guards.type[i]].cost.crystal * town->guards.count[i]);
cost_res[5] = (o_pCreatureInfo[GetCreatureGrade(town->guards.type[i])].cost.jems * town->guards.count[i]) - (o_pCreatureInfo[town->guards.type[i]].cost.jems * town->guards.count[i]);
cost_res[6] = (o_pCreatureInfo[GetCreatureGrade(town->guards.type[i])].cost.gold * town->guards.count[i]) - (o_pCreatureInfo[town->guards.type[i]].cost.gold * town->guards.count[i]);
if (me->resourses.wood >= cost_res[0] && me->resourses.mercury >= cost_res[1] && me->resourses.ore >= cost_res[2] && me->resourses.sulfur >= cost_res[3] && me->resourses.crystal >= cost_res[4] && me->resourses.jems >= cost_res[5] && me->resourses.gold >= cost_res[6])
{
me->resourses.wood -= cost_res[0];
me->resourses.mercury -= cost_res[1];
me->resourses.ore -= cost_res[2];
me->resourses.sulfur -= cost_res[3];
me->resourses.crystal -= cost_res[4];
me->resourses.jems -= cost_res[5];
me->resourses.gold -= cost_res[6];
town->guards.type[i] = GetCreatureGrade(town->guards.type[i]);
}
}
}
}
}
return true;
}
return false;
}
// улучшение одного стека у героя
_bool_ Y_AutoGradeMonInTownOne_H(_Town_* town, char type, char i)
{
_Player_* me = o_GameMgr->GetMe();
if (!me->IsActive()) return false;
int cost_res[7];
_Hero_* hero = 0;
if (type == 1)
hero = o_GameMgr->GetHero(town->up_hero_id);
else hero = o_GameMgr->GetHero(town->down_hero_id);
if (me->id != hero->owner_id || hero->army.type[i] == -1 ) return false;
if ( GetCreatureGrade(hero->army.type[i]) != -1 && hero->army.type[i] != 158 )
{
if( town->IsBuildingBuilt(37 + o_pCreatureInfo[GetCreatureGrade(hero->army.type[i])].level, 1) && town->type == o_pCreatureInfo[hero->army.type[i]].town )
{
cost_res[0] = (o_pCreatureInfo[GetCreatureGrade(hero->army.type[i])].cost.wood * hero->army.count[i]) - (o_pCreatureInfo[hero->army.type[i]].cost.wood * hero->army.count[i]);
cost_res[1] = (o_pCreatureInfo[GetCreatureGrade(hero->army.type[i])].cost.mercury * hero->army.count[i]) - (o_pCreatureInfo[hero->army.type[i]].cost.mercury * hero->army.count[i]);
cost_res[2] = (o_pCreatureInfo[GetCreatureGrade(hero->army.type[i])].cost.ore * hero->army.count[i]) - (o_pCreatureInfo[hero->army.type[i]].cost.ore * hero->army.count[i]);
cost_res[3] = (o_pCreatureInfo[GetCreatureGrade(hero->army.type[i])].cost.sulfur * hero->army.count[i]) - (o_pCreatureInfo[hero->army.type[i]].cost.sulfur * hero->army.count[i]);
cost_res[4] = (o_pCreatureInfo[GetCreatureGrade(hero->army.type[i])].cost.crystal * hero->army.count[i]) - (o_pCreatureInfo[hero->army.type[i]].cost.crystal * hero->army.count[i]);
cost_res[5] = (o_pCreatureInfo[GetCreatureGrade(hero->army.type[i])].cost.jems * hero->army.count[i]) - (o_pCreatureInfo[hero->army.type[i]].cost.jems * hero->army.count[i]);
cost_res[6] = (o_pCreatureInfo[GetCreatureGrade(hero->army.type[i])].cost.gold * hero->army.count[i]) - (o_pCreatureInfo[hero->army.type[i]].cost.gold * hero->army.count[i]);
if (me->resourses.wood >= cost_res[0] && me->resourses.mercury >= cost_res[1] && me->resourses.ore >= cost_res[2] && me->resourses.sulfur >= cost_res[3] && me->resourses.crystal >= cost_res[4] && me->resourses.jems >= cost_res[5] && me->resourses.gold >= cost_res[6])
{
me->resourses.wood -= cost_res[0];
me->resourses.mercury -= cost_res[1];
me->resourses.ore -= cost_res[2];
me->resourses.sulfur -= cost_res[3];
me->resourses.crystal -= cost_res[4];
me->resourses.jems -= cost_res[5];
me->resourses.gold -= cost_res[6];
hero->army.type[i] = GetCreatureGrade(hero->army.type[i]);
return true;
}
}
}
return false;
}
// улучшение одного стека в городе без героя
_bool_ Y_AutoGradeMonInTownOne_T(_Town_* town, char type, char i)
{
_Player_* me = o_GameMgr->GetMe();
if (!me->IsActive()) return false;
int cost_res[7];
if (me->id != town->owner_id || town->guards.type[i] == -1 ) return false;
if ( GetCreatureGrade(town->guards.type[i]) != -1 && town->guards.type[i] != 158 )
{
if( town->IsBuildingBuilt(37 + o_pCreatureInfo[GetCreatureGrade(town->guards.type[i])].level, 1) && town->type == o_pCreatureInfo[town->guards.type[i]].town )
{
cost_res[0] = (o_pCreatureInfo[GetCreatureGrade(town->guards.type[i])].cost.wood * town->guards.count[i]) - (o_pCreatureInfo[town->guards.type[i]].cost.wood * town->guards.count[i]);
cost_res[1] = (o_pCreatureInfo[GetCreatureGrade(town->guards.type[i])].cost.mercury * town->guards.count[i]) - (o_pCreatureInfo[town->guards.type[i]].cost.mercury * town->guards.count[i]);
cost_res[2] = (o_pCreatureInfo[GetCreatureGrade(town->guards.type[i])].cost.ore * town->guards.count[i]) - (o_pCreatureInfo[town->guards.type[i]].cost.ore * town->guards.count[i]);
cost_res[3] = (o_pCreatureInfo[GetCreatureGrade(town->guards.type[i])].cost.sulfur * town->guards.count[i]) - (o_pCreatureInfo[town->guards.type[i]].cost.sulfur * town->guards.count[i]);
cost_res[4] = (o_pCreatureInfo[GetCreatureGrade(town->guards.type[i])].cost.crystal * town->guards.count[i]) - (o_pCreatureInfo[town->guards.type[i]].cost.crystal * town->guards.count[i]);
cost_res[5] = (o_pCreatureInfo[GetCreatureGrade(town->guards.type[i])].cost.jems * town->guards.count[i]) - (o_pCreatureInfo[town->guards.type[i]].cost.jems * town->guards.count[i]);
cost_res[6] = (o_pCreatureInfo[GetCreatureGrade(town->guards.type[i])].cost.gold * town->guards.count[i]) - (o_pCreatureInfo[town->guards.type[i]].cost.gold * town->guards.count[i]);
if (me->resourses.wood >= cost_res[0] && me->resourses.mercury >= cost_res[1] && me->resourses.ore >= cost_res[2] && me->resourses.sulfur >= cost_res[3] && me->resourses.crystal >= cost_res[4] && me->resourses.jems >= cost_res[5] && me->resourses.gold >= cost_res[6])
{
me->resourses.wood -= cost_res[0];
me->resourses.mercury -= cost_res[1];
me->resourses.ore -= cost_res[2];
me->resourses.sulfur -= cost_res[3];
me->resourses.crystal -= cost_res[4];
me->resourses.jems -= cost_res[5];
me->resourses.gold -= cost_res[6];
town->guards.type[i] = GetCreatureGrade(town->guards.type[i]);
return true;
}
}
}
return false;
}
// автоулучшение существ в городе по ЛКМ+A
_int_ __stdcall Y_AutoGradeMonInTown(LoHook* h, HookContext* c)
{
if (/* !o_IsOnlineGame && */ GetKeyState(65)<0 )
{
_bool_ done = 0;
_Town_* town = o_TownMgr->town;
int klick_id = c->edi;
if(klick_id == 125 && town->down_hero_id != -1)
done = Y_AutoGradeMonInTownFunc(town, 2);
if(klick_id == 123 && town->up_hero_id != -1)
done = Y_AutoGradeMonInTownFunc(town, 1);
if(klick_id == 123 && town->up_hero_id == -1)
done = Y_AutoGradeMonInTownFunc(town, 0);
if( klick_id >= 140 && klick_id <= 146 && town->down_hero_id != -1 )
done = Y_AutoGradeMonInTownOne_H(town, 2, klick_id - 140);
if( klick_id >= 115 && klick_id <= 121 && town->up_hero_id != -1 )
done = Y_AutoGradeMonInTownOne_H(town, 1, klick_id - 115);
if( klick_id >= 115 && klick_id <= 121 && town->up_hero_id == -1 )
done = Y_AutoGradeMonInTownOne_T(town, 1, klick_id - 115);
// обновить экран города
if (done)
{
CALL_1(void, __thiscall, 0x5D5930, *(int*)0x69954C); // отключить жёлтую обводку
CALL_1(void, __thiscall, 0x5D5810, o_TownMgr); // обновить экран города
c->return_address = 0x5D460F;
return NO_EXEC_DEFAULT;
}
}
return EXEC_DEFAULT;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
static _bool_ plugin_On = 0;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
if (!plugin_On)
{
plugin_On = 1;
// Создаём объекты patcher_x86.
_P = GetPatcher();
_PI = _P->CreateInstance("HD_Plugin.AutoGradeMonInTown");
_PI->WriteLoHook(0x5D45FD, Y_AutoGradeMonInTown);
}
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
Автор: Ben 22 Aug 2017, 06:37
Цитата(fireman @ 21 Aug 2017, 23:09)
Ben, закинь в гугл/яндекс диск в ближайшие лет 5 не умрёт точно
https://yadi.sk/d/Cgr-pvMX3NQmFW
Автор: Iv 22 Aug 2017, 16:33
Напоминаю:
Цитата(feanor @ 25 Jul 2012, 11:30)
Все обсуждения - в http://forum.df2.ru/index.php?showtopic=30850.
..
Просьба здесь особо не обсуждать. Мне куда более доставляет складывать добро в чистую тему.
Автор: Ben 02 Sep 2017, 17:46
Модификация Зыбучих песков и Мин
Dll плагин к HD моду.
Теперь количество этих объектов не 4/6/8, а 6/8/10.
С помощью SPTRAITS.txt это недостижимо.
Код
#include "..\..\include\homm3.h"
Patcher* _P;
PatcherInstance* _PI;
int __stdcall quicksandSpell(LoHook* h, HookContext* c)
{
int peski[] = {6, 6, 8, 10};
c->edi = peski[c->esi];
return EXEC_DEFAULT;
}
int __stdcall minesSpell(LoHook* h, HookContext* c)
{
int miny[] = {6, 6, 8, 10};
c->edx = miny[c->esi];
return EXEC_DEFAULT;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
static _bool_ plugin_On = 0;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
if (!plugin_On)
{
plugin_On = 1;
_P = GetPatcher();
_PI = _P->CreateInstance("HD.Plugin.QuicksandMines");
_PI->WriteLoHook(0x5A066B, quicksandSpell);
_PI->WriteLoHook(0x5A0852, minesSpell);
}
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
Сама Dll:
https://yadi.sk/d/5iwgKir_3NQmdY
Автор: Ben 27 Sep 2017, 18:41
Улучшение вторичного навыка Обучение
Dll плагин к HD моду.
https://yadi.sk/d/MpGGkmG33NGbsE
UPD внес небольшое исправление по совету AlexSpl - поменял тип int на _word_ в строке с объявлением heroLevel.
https://yadi.sk/d/Tek6oZpq3NQmxA
Теперь герой с данным навыком с увеличением количества опыта увеличивает свой уровневый отрыв от героя без данного навыка.
Сравнительное движение героев по уровням:
Герой с навыком: 1->3->8->16->21->25->37->40->46
Герой без навыка: 1->3->7->11->14->17->27->30->34
Суть нововведения в том, что количество дополнительного опыта рассчитывается исходя из текущего уровня героя
(это количество линейно зависит от текущего уровня, степень же навыка является дополнительным множителем).
Код
#include "..\..\include\homm3.h"
Patcher* _P;
PatcherInstance* _PI;
static _bool_ plugin_On = 0;
int __stdcall changeLearningPower(LoHook* h, HookContext* c)
{
char learningSkill = *(char*)(c->ecx + 0xDE);
_word_ heroLevel = *(_word_*)(c->ecx + 0x55);
float multiplier = learningSkill * heroLevel / (float)15.0;
*(float*)(c->ebp - 4) = (float)multiplier;
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("HD.Plugin.LearningSkill");
_PI->WriteLoHook(0x4E4ACD, changeLearningPower);
}
}
return TRUE;
}
Автор: Ben 30 Sep 2017, 09:02
Улучшение навыка Орлиный глаз
Авторы: AlexSpl, Ben
Dll плагин к HD моду.
https://yadi.sk/d/8xAXgEpk3NLf8f
UPD Обновленная ссылка с версией в релизной конфигурации (предыдущая могла не работать на каких-то системах)
https://yadi.sk/d/RVqiFWfr3NQNWi
Теперь навык действует не после битвы, а до, в книгу заклинаний копируются
заклинания противника, с определенной вероятностью по схеме 30/35/40% для каждого заклинания
в зависимости от степени навыка (герою доступны заклинания 1-2/1-3/1-4 уровней в зависимости от
степени навыка, как в оригинальной игре). Влияние артефактов и специальности учитывается.
В сетевой игре работает.
Код
#define _CRT_RAND_S
#define _CRT_SECURE_NO_WARNINGS
#include "..\..\include\homm3.h"
Patcher* _P;
PatcherInstance* _PI;
static _bool_ plugin_On = 0;
struct PicStruc {
int type; // тип картинки (9 - заклинание)
int id; // ID картинки
};
// кастомный _List_
struct List {
_ptr_ Creation;
PicStruc* Data;
PicStruc* EndData;
_ptr_ EndMem;
};
int captionAddr;
bool dlgFirst[] = {true, true};
// Заголовок в каждом диалоге
int __stdcall saveCaption(LoHook* h, HookContext* c)
{
captionAddr = c->ecx;
return EXEC_DEFAULT;
}
int __stdcall captionFix(LoHook* h, HookContext* c)
{
*(int*)(c->ebp - 0x14) = captionAddr;
return EXEC_DEFAULT;
}
int showSpellDlg(_Hero_* hero, int spells[], int nPics)
{
if ( !nPics ) return 0;
List picList;
// Динамический массив картинок
// Первый и последний элемент резервируем для полей Creation и EndData/EndMem соответственно
PicStruc* pic = new PicStruc[nPics + 2];
for (int i = 0; i < nPics; ++i) {
pic[i + 1].type = 9;
pic[i + 1].id = spells[i];
}
picList.Creation = (_ptr_)pic + 4;
picList.Data = pic + 1; // Адрес первого элемента в списке
picList.EndData = picList.Data + nPics; // Адрес следующего за последним элементом в списке байта
picList.EndMem = (_ptr_)picList.EndData;
sprintf(o_TextBuffer, "Благодаря навыку {Орлиный глаз}, {%s} выучил%s следующ%s заклинан%s:",
hero->name, hero->sex ? "а" : "", nPics > 1 ? "ие" : "ее", nPics > 1 ? "ия" : "ие");
CALL_5(unsigned int, __fastcall, 0x4F7D20, o_TextBuffer, &picList, -1, -1, 0);
delete [] pic;
return 0;
}
int getEagleEyeSpells(_Hero_* hero, _Hero_* heroDonor, int spells[])
{
int n = 0;
unsigned int eagleEyeProb = (unsigned int)(CALL_1(float, __thiscall, 0x4E4690, hero) * 100.0);
for (_Spell_* iSpell = o_Spell + SPL_QUICKSAND; iSpell <= o_Spell + SPL_AIR_ELEMENTAL; ++iSpell)
{
int i = iSpell - o_Spell;
if ( heroDonor->spell_level[i] && !hero->spell[i] )
{
if ( iSpell->level <= hero->second_skill[HSS_EAGLE_EYE] + 1 )
{
unsigned int dice;
rand_s(&dice);
dice = (unsigned int)((double)dice / ((double)UINT_MAX + 1) * 100.0) + 1;
if ( dice <= eagleEyeProb )
{
spells[n++] = i;
hero->spell[i] = 1;
hero->spell_level[i] = 1;
}
}
}
}
return n;
}
int __stdcall eagleEyeMain(LoHook* h, HookContext* c)
{
_Hero_* hero[] = {o_BattleMgr->hero[ATTACKER], o_BattleMgr->hero[DEFENDER]};
if ( hero[ATTACKER] && hero[DEFENDER] )
{
int spells[70];
for (int i = ATTACKER; i <= DEFENDER; ++i)
{
if ( dlgFirst[i] && o_BattleMgr->current_side == i && hero[i]->second_skill[HSS_EAGLE_EYE] &&
hero[i]->doll_art[AS_SPELL_BOOK].id == AID_SPELL_BOOK )
{
dlgFirst[i] = false;
// Учим заклинания всегда.
int n = getEagleEyeSpells(hero[i], hero[1 - i], spells);
// Но диалог показываем только для игрока-человека:
// в хотсите - диалог для обоих героев, в сетевой игре - только для своего героя.
if ( o_GameMgr->GetPlayer(hero[i]->owner_id)->IsHuman() ) {
int id = o_GameMgr->GetMeID();
if ( !o_NetworkGame || hero[i]->owner_id == id ) {
o_ActivePlayerID = hero[i]->owner_id;
showSpellDlg(hero[i], spells, n);
o_ActivePlayerID = id;
}
}
}
}
}
return EXEC_DEFAULT;
}
int __stdcall eagleEyeSetGlobalFlags(LoHook* h, HookContext* c)
{
dlgFirst[ATTACKER] = true;
dlgFirst[DEFENDER] = true;
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("HD.Plugin.NewEagleEye");
// Меняем коэффициенты
float eagleEyeCoefs[] = {0.00f, 0.30f, 0.35f, 0.40f};
_PI->WriteDword(0x63EA2C, (int&)eagleEyeCoefs[1]);
_PI->WriteDword(0x63EA30, (int&)eagleEyeCoefs[2]);
_PI->WriteDword(0x63EA34, (int&)eagleEyeCoefs[3]);
// Убираем оригинальный эффект
_PI->WriteHexPatch(0x469C23, "EB");
_PI->WriteHexPatch(0x476996, "E9 DD 01 00 00");
// Фиксим заголовок диалога
_PI->WriteLoHook(0x4F7D49, saveCaption);
_PI->WriteLoHook(0x4F7D54, captionFix);
_PI->WriteLoHook(0x462C7D, eagleEyeSetGlobalFlags);
_PI->WriteLoHook(0x477C00, eagleEyeMain);
}
}
return TRUE;
}
Автор: igrik 20 Sep 2018, 22:02
WoG Native Dialogs
(Вог диалоги в родном исполнении)
Основная цель: поддержка мастшабирования высоких разрешений при игре с HD-модом
Сделано:
(
)
(
)
(
)
(
)
(
)
(
)
(
)
(
)
Автор:
igrikЯзык: Rus/Eng
Формат: самораспаковывающийся архив
Поддерживаемые версии: ERA
Способ установки: указать корневую папку ERA
https://dl.dropboxusercontent.com/s/t771vm40znr1qbf/WoG_Native_Dialogs.exe
Автор: igrik 16 Nov 2018, 08:42
Защита артефактов, пандор и свитков
https://dl.dropboxusercontent.com/s/95j4nt3wajorg1j/ArtGuard.dll (41.5kb)
Совместимость: SoD, ERA
Автор: igrik
Описание: теперь артефакты, ящики пандор и свитки защищены рядом стоящими монстрами
(монстр защищает объекты согласно красной рамки):
Автор: Richter 11 Feb 2019, 15:15
Небольшая модификация добавляющая статистику пройденного расстояния героя, просмотр по нажатию клавиши "X"
Автор:Richter
1 клетка карты равна 0,85714 км.
Содержимое архива положить в папку "Мods"
https://drive.google.com/file/d/13G4S9x6e5o869H2tCbDW3yJaFJYobi4x/view?usp=sharing (950 байт)
Автор: suftfree 26 Apr 2020, 15:21
Модификация добавляющая огромное количество новых меню, подменю, интерфейс, обои, экраны загрузки
Автор: Suftfree, Berserker
Язык: ENG
Формат: папка с модом.
Установка: кинуть в папку Mod, включить мод в менеджере модификаций ERA.
Здравствуйте я завершил улучшение модификации Berserkera.
Добавлена поддержка случайных меню, изменены фоны, экраны загрузки, экран сетевого меню.
Версия BETA 0.1 - Preview eng new interface - потом добавлю стандартный интерфейс ENG и RUS
При начале игры выбирается 1 случайное меню, 2 случайных подменю и случайный экран загрузки.
Скачать:
https://yadi.sk/d/HpgEpfirNktdOw
Форум Invision Power Board (http://nulled.cc)
© Invision Power Services (http://nulled.cc)