Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Небольшие моды и плагины.
DF2 :: ФОРУМЫ > Игровые форумы > Heroes of Might & Magic III > Моды
feanor
Эта тема предназначена для небольших модов, не требующих оформления отдельной темы.
Все обсуждения - в соседней теме.

При выкладке указывайте:
  • краткое описание. Скриншот - факультативно
  • авторство (если не свое - особенно)
  • формат (инсталлятор, архив или одиночный плагин),
  • язык (или его отсутствие в случае безтекстового мода).
  • разумеется, ссылку на скачивание. Не рекомендуются как zalil, rghost и подобные обменники с кратким сроком жизни файла, так и depositfiles, letitbit и прочие многоэтапные файлопохранилища.


Замена воговского таймера для Эры

Всегда убивали воговские таймеры (которые TM, не TL).

Их мало (сто, из них изрядная часть зарезервирована), они перегружены и вообще мастдай.
В дллках оно еще более усугубляется - совершенно не хочется делать лишний хэндл для инициализации таймера, долбаться с его резервированием etc etc..


Поэтому для удобства написания дллок я набросал простенькую библиотеку, которая хукает RunTimer и посылает сообщение плагинам и ERM'у.
Требует Бараторчевский патчер, ибо он трушный.

Плагин: timerevent.era
Проект: 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
Обработка копания (granite.dll)

Плагин: granite.era
Проект: 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
Работа с датой (malachite.dll)

Плагин: 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


Контроль восьмого слота (obsidian.dll)

Плагин: 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
Отрицательная удача (badluck.dll) (с) Sav, feanor

Плагин (SoD, Era): badluck.era, зеркало



Чуть-чуть информации о функционировании удачи вообще:

"удачливость" стека определяется во время атаки (собстна, немногим ранее проигрывания анимации) и записывается в структуру стека, в поле со смещением +0x70 (1 - удача, 0 - обычная атака, добавляемое плагином -1 - неудача).
Проверять через ERM, соответственно, можно пресловутой командой !!BM:G, подсунув ей в качестве номера заклинания индекс -74.

Потом, при нанесении ущерба это поле проверяется и уже подсчитывается собственно ущерб.
feanor
Настраиваемые имена классов (turquoise.dll)

Плагин: 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
Вызовы элементалей (summoniots.dll)

Плагин: 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
Гильдия воров вместо "Братства меча" в Замке.

Надстройка над таверной теперь исполняет функции представительства Гильдии Воров: при её постройке соответствующее меню в таверне будет показывать полную информацию о противниках и союзниках. Цена строения - прежняя, 500 золота и 5 дерева.

Автор: feanor
Язык: русский (изменена одна строчка в bldgspec.txt)
Формат: zip-архив с папкой мода.

thiefguild.zip (3,32 Кб)
Iv
Другие флаги игроков


Автор: Iv (с участием Algor)
Язык: нет
Формат: pac-файл для Era 2.*. Кинуть в папку любого мода

http://sites.google.com/site/hommfun/files...ive%20flags.pac (390 кб)
feanor
Несколько небольших патчей.

Автор: feanor
Язык: нет
Формат: bin-файлы. Установка через копирование в eraplugins/afterwog любого мода.

Нет подземелий в подземельях.

Тип поля боя на подземном уровне будет определяться типом почвы. В оригинале все бои в подземке проходили на подземной почве.
no_underground_at_underground.bin

Исправление вылета при проигрыше битвы в ERM-событии

При проигрыше битвы, вызванной в триггере !?HM, игра вылетала. Теперь - нет.
fix_encounter_crash.bin

Отключение исключений при старте карты

При рестарте карты OllyDbg отвлекается на какое-то исключение, что вынуждает переключаться между игрой и отладчиком. Теперь - нет.
Плагин нужен лишь при анализе игры! Не уверен в отсутствии побочных эффектов.
no_raise_exception_at_map_loading.bin
Iv
Зомбивод (39 б)
Автор: Master of puppets
Язык: нет
Формат: bin-арник для Эры. Кинуть в папку eraplugins/afterwog
Описание: Стракер поднимает не скелетов, а зомби



feanor
Часы реального времени.



ПКМ по дате на экране карты выведет реальное время и заряд батареи (при наличии таковой). Для играющих в полноэкранном режиме.

Автор: feanor
Язык: английский
Формат: erm-скрипт. Установка через копирование в data/s любого мода.

clock.erm (1,13 Кб)
feanor
Еженедельная газета "Голос из-под стойки".



В начале каждой недели в чат выводятся текущие слухи и имена героев-наемников.

Автор: feanor
Язык: русский.
Формат: zip-архив с папкой мода.

daily_event.zip (5.36 кб)
Iv
-Why do you call your software "beta"?
-'coz it's beta than nothing.


Замена вызовов, версия 0.3
Нажмите для просмотра прикрепленного файла
Вызовы элементалей заменяются на вызовы других существ
В мод включена измененная раскладка заклинаний.

Автор: Iv (с участием Algor)
Язык: русский
Формат: архив с папкой мода для Эры 2.х

https://dl.dropboxusercontent.com/u/5852703.../H3IVsummon.exe
igrik
Рюкзак героя
Версия 1.04.
Автор: igrik
Язык: русский и английский
Формат: архив с папкой мода для ERA II

Спасибо Berserker, baratoch.
Функция аналогичная функции в HDmod, но с меньшим функционалом (не реализована и не планируется передача артов по Ctrl, Shift, Alt)
Совместима с большинством модов (за исключением Феникс-Мода! ввиду пересечения расположения кнопок)
Поддерживает Hot Seat, TCP/IP, новые артефакты от feanor'a "emerald.dll v2.01" и т.п.
Спасибо: Berserker, baratorch.
Скачать Rus
Скачать Eng







*********
Исправлено:
[+] Нормальное отображение описания артефактов! За что огромное спасобо Berserker'у!
[-] Больше не используется файл "artifact.def", который мог перекрывать аналогичный из других модов.
[-] Исправлен баг, при котором игра вылетала если в рюкзаке лежала книга заклинаний.
[-] Исправлен баг вследствие некорректного действия команды "HE:A1".
[+] Улучшена подложка
[+] Подсказка при наведении мыши на элемент диалога. (для временного хранения используются переменные z1 - z65)
Iv
Случайные нейтральные герои

Нейтралы с вероятностью 10/20/30/40/50% в зависимости от уровня сложности получают героя-предводителя,
который будет сражаться на их стороне в бою против игрока-человека.


Автор: Algor, Iv
Формат: sfx-архив с папкой мода.

Randomhero.exe (306 Кб)
feanor
Статуя Ленина в Инферно вместо Бога Огня

Автор: feanor
Формат: архив с папкой мода.



https://dl.dropboxusercontent.com/u/61759222/HoMM/lenin.zip
Iv
Родная грязь (dirt) для Подземелья

Маленький патчик для рандомок без подземного уровня. На них Подземелье генерится на грязи, но бонуса родной земли не получает. Патчик исправляет эту несправедливость.

Автор: Iv
Формат: bin-файл для Era II. Класть в \EraPlugins\AfterWoG\
Orzie
Культ Вождя Мирового Пролетариата

Авторы: feanor, Orzie, Docent Picolan, Bes
Формат: SFX-архив мода для ERA II, спрашивающий директорию установленной игры.

Апгрейд мода "Статуя Ленина в Инферно вместо Бога Огня"
Определённые строения в Инферно, Некрополисе и Замке будут содержать различные образы вождя пролетариата - В.И.Ленина.



https://app.box.com/s/qmfj3km81roz85tutojf
feanor
Магические сундуки

Сундуки с артефактами теперь отличаются по виду от сундуков с золотом.

Автор: Axolotl, feanor
Язык: нет
Формат: zip-архив с папкой мода.

magechest.zip (14 Кб)

feanor
Вуду-гарпии.

Иногда гарпии-ведьмы будут атаковать с места, не приближаясь к противнику. Анимация этой атаки будет отличаться от обычной.

Автор: feanor
Язык: нет
Формат: erm-скрипт. Установка через копирование в data/s любого мода.

voodoo.erm (1 Кб)

feanor
Иной формат даты.

Дни недели и месяцы именуются, а не нумеруются.

Автор: feanor
Язык: русский
Формат: dll-плагин. Установка через копирование в /eraplugins любого мода.

human_month_names.dll (8,5 Кб)

feanor
Иные виды снарядов-лучей.

Архимаги стреляют молниеподобными лучами, бехолдеры - лучами разноцветными (семь цветов радуги).

Автор: feanor
Язык: нет
Формат: erm-скрипт. Установка через копирование в data/s любого мода.
Старая тема: http://forum.df2.ru/index.php?showtopic=22685

rays.erm (1 Кб)



hippocamus
Приручённые нейтралы

Большинство нейтралов теперь принадлежат фракциям, к которым они сродны.
Орки на кабанах - Цитадель
Мумии - Некрополис
Тролли - Крепость
Крестьне - Замок
Воры - Темница
Хоббиты - Башня

Внефракционными остались высшие драконы, чародеи, снайперы и кочевники (есть соблазн приписать их к Сопряжению - по антуражу подходят, но стоит ли?)

автор: hippocamus
формат: bin для SOD, HotA + HD (возможно Era)
язык: отсутствует
Скачать (76 байт)


Кампанейские герои доступны как стартовые

На случайных картах можно стартовать любым героем

автор: возможно, МоП (я взял из ExeBuilder и перебил адреса, адаптировал под ХД)
формат: bin для SoD+HD (под Хоту не работает)
язык: отсутствует
Скачать (301 байт)
Iv
Больше колодцев для бога колодцев

Маленький патчик для рандомок. На карте генерится больше колодцев. Иногда даже слишком много

Автор: Iv (инфа от Sav)
Формат: bin-файл для Era II. Класть в \EraPlugins\AfterWoG\ любого активного мода

feanor
В честь минувших праздников полового диморфизма..

Зависящие от пола имена классов.

Автор: feanor
Язык: русский
Формат: папка с модом.

gcn.zip (6 Кб)

feanor
Анимированные существа на поле боя (порт из WoG 3.59)

Автор: sergroj
Портировано: feanor в рамках проекта TSW; разрешение автора получено.
Формат: папка с модом.

InstantAnimation.zip, 7 Кб

Скорости анимаций настраиваются индивидуально для каждого типа существ в файле anims.ini; существующие параметры взяты из блока monanim0 хотовского HotA.dat (версия 1.3.4), для вог-монстров взято по подобию.
Помимо этого, есть глобальный множитель, позволяющий ускорить или замедлить анимацию для всех монстров без пересчета всех параметров.
igrik
Цифровое отображение морали и удачи в окне стека.
Как в MOP

автор: igrik
язык: нет
формат: dll (плагин для HD-мода)
версии: SoD/WoG/ERA

NumMoralLuck.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
Новая версия заклинания Городской портал

Авторы: 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
А где сама DLL?

Кстати забыл. Тестировал. Багов не обнаружил, поэтому включил и в мультиплеере.
Автоулучшение существ в городе.

автор: [igrik]
язык: нет
формат: dll (плагин для HD-мода)
версии: SoD/HotA/ERA

HD_Plugin.AutoGradeMonInTown (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
Цитата(fireman @ 21 Aug 2017, 23:09) *
Ben, закинь в гугл/яндекс диск в ближайшие лет 5 не умрёт точно


https://yadi.sk/d/Cgr-pvMX3NQmFW
Iv
Напоминаю:

Цитата(feanor @ 25 Jul 2012, 11:30) *
Все обсуждения - в соседней теме.
..
Просьба здесь особо не обсуждать. Мне куда более доставляет складывать добро в чистую тему.
Ben
Модификация Зыбучих песков и Мин

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
Улучшение вторичного навыка Обучение

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
Улучшение навыка Орлиный глаз

Авторы: 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
WoG Native Dialogs
(Вог диалоги в родном исполнении)
Основная цель: поддержка мастшабирования высоких разрешений при игре с HD-модом

Сделано:
()
()
()
()
()
()
()
()


Автор: igrik
Язык: Rus/Eng
Формат: самораспаковывающийся архив
Поддерживаемые версии: ERA
Способ установки: указать корневую папку ERA

Скачать
igrik
Защита артефактов, пандор и свитков

Скачать (41.5kb)

Совместимость: SoD, ERA
Автор: igrik
Описание: теперь артефакты, ящики пандор и свитки защищены рядом стоящими монстрами
(монстр защищает объекты согласно красной рамки):




Richter
Небольшая модификация добавляющая статистику пройденного расстояния героя, просмотр по нажатию клавиши "X"
Автор:Richter

1 клетка карты равна 0,85714 км.
Содержимое архива положить в папку "Мods"
X_button_mod (950 байт)
suftfree
Модификация добавляющая огромное количество новых меню, подменю, интерфейс, обои, экраны загрузки
Автор: Suftfree, Berserker
Язык: ENG
Формат: папка с модом.
Установка: кинуть в папку Mod, включить мод в менеджере модификаций ERA.

Здравствуйте я завершил улучшение модификации Berserkera.
Добавлена поддержка случайных меню, изменены фоны, экраны загрузки, экран сетевого меню.
Версия BETA 0.1 - Preview eng new interface - потом добавлю стандартный интерфейс ENG и RUS
При начале игры выбирается 1 случайное меню, 2 случайных подменю и случайный экран загрузки.
Скачать:
https://yadi.sk/d/HpgEpfirNktdOw
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2025 IPS, Inc.