Объявления

Друзья, если не получается зарегистрироваться, напишите на почту vdv_forever@bk.ru.
Я оторву свою задницу от всех дел и обязательно Вас активирую! :smile10:
Добро пожаловать на геройский форум! :smile25:

Пользовательские плагины для HD мода

Герои Меча и Магии III: Возрождение Эрафии, Герои Меча и Магии III Дыхание Смерти, Герои Меча и Магии III Клинок Армагеддона, Герои Меча и Магии III Хроники Героев
offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 2185 раз.

Re: Пользовательские плагины для HD мода

Сообщение AlexSpl » 02 ноя 2022, 19:09

Автор: AlexSpl

Название: FairCancelSpells

Описание: Заклинания Dispel и Cure снимают только последнее наложенное заклинание/эффект. В случае, если последнее заклинание/эффект невозможно снять (например, последний эффект - Poison для Dispel или какой-нибудь баф для Cure), снимается ближайшее заклинание/эффект, которое можно снять.

 Код
Код: Выделить всё
#define _CRT_SECURE_NO_WARNINGS
#define _SECURE_SCL 0
#define _HAS_ITERATOR_DEBUGGING 0

#pragma warning(disable : 4005)
#pragma warning(disable : 4010)

#include "stdafx.h"
#include <vector>
#include "..\..\HotA\homm3.h"

Patcher* _P;
PatcherInstance* _PI;

#pragma pack(push, 4)
class stdDequeIterator {
public:
   int* first;
   int* last;
   int* node;
   int** map;
};

class stdDeque {
public:
   char allocator;
   stdDequeIterator itFirst;
   stdDequeIterator itLast;
   int** map;
   int mapSize;
   int size;
};

struct SLimitData
{
   int MinX;
   int MinY;
   int MaxX;
   int MaxY;
};

struct TObstacleInfo
{
   unsigned long backgroundMask;
   unsigned char height;
   unsigned char width;
   unsigned char numSquares;
   unsigned char underlay;
   char sOffsets[8];
   const char* FileName;
};

// Actual types are commented, as we don't need them in this plugin
struct TObstacle
{
   // CSprite* sprite;
   int* sprite;
   TObstacleInfo* info;
   unsigned char grid_index;
   char owner;
   bool is_visible;
   int damage;
   int duration;
   // TSpellEffectID dispel_effect;
   int dispel_effect;
};

class hexcell {
public:
   short refX;
   short refY;
   short hexULX;
   short hexULY;
   short hexBRX;
   short hexBRY;
   short fullHexBRY;
   unsigned int attributes;
   int obstacleIndex;
   char armyGrp;
   char armyIndex;
   char partOfDouble;
   int iBodiesInHex;
   char deadArmyGrp[14];
   char deadArmyIndex[14];
   char deadPartOfDouble[14];
   bool bValidMove;
   unsigned char front_move;
   unsigned char mouse_shaded;
   char background_offset;
   SLimitData obstacleLimitData;
   SLimitData cloudLimitData;
};
#pragma pack(pop)

class army : public _BattleStack_ {
public:
   inline void CancelIndividualSpell(int SpellID) {
      CALL_2(void, __thiscall, 0x444230, this, SpellID);
   }
};

void debugStrWin(const char* format, ...)
{
   va_list args;
   va_start(args, format);

   vsprintf(o_TextBuffer, format, args);
   ShowMessage(o_TextBuffer);
   
   va_end(args);
}

#define SPL_DISPEL 35
#define SPL_STONE_GAZE 70
#define SPL_DISEASE 73
#define SPL_AGE 75
#define SPL_DISPEL_HELPFUL_SPELLS 78

int __stdcall fixCure(LoHook* h, HookContext* c)
{
   army* Army = (army*)c->esi;
   Army->lost_hp = c->eax;

   if (Army->active_spells_count)
   {
      stdDeque* spellDeque = (stdDeque*)(c->esi + 0x420);
     
      for (int i = spellDeque->size; i > 0; --i)
      {
         // You can do so because the size of spellDeque's chunk is 4096 bytes,
         // and this is more than enough for all possible spells
         int spell = spellDeque->itFirst.node[i - 1];
         
         if (spell == SPL_CURSE || spell == SPL_WEAKNESS || spell == SPL_SORROW || spell == SPL_MISFORTUNE ||
            spell == SPL_SLOW || spell == SPL_BERSERK || spell == SPL_HYPNOTIZE || spell == SPL_FORGETFULNESS ||
            spell == SPL_BLIND || spell == SPL_STONE_GAZE || spell == SPL_POISON || spell == SPL_DISEASE ||
            spell == SPL_PARALYZE || spell == SPL_AGE)
         {
            Army->CancelIndividualSpell(spell);
            break;
         }
      }
   }
   
   c->return_address = 0x4462FD;
   return NO_EXEC_DEFAULT;
}

int __stdcall spellCastWorkChance(LoHook* h, HookContext* c)
{
   army* Army = *(army**)(c->ebp + 0x10);
   int spell = *(int*)(c->ebp + 8);

   if (spell == SPL_DISPEL && Army && Army->active_spells_count)
   {
      stdDeque* spellDeque = (stdDeque*)(c->edi + 0x420);

      for (int i = spellDeque->size; i > 0; --i)
      {
         int spell = spellDeque->itFirst.node[i - 1];

         if (spell != SPL_POISON)
            return EXEC_DEFAULT;
      }

      std::vector<TObstacle>* Obstacles = o_BattleMgr->PField<std::vector<TObstacle>>(0x13D58);

      for (std::vector<TObstacle>::iterator it = Obstacles->begin(); it != Obstacles->end(); ++it)
         if (o_BattleMgr->PField<hexcell>(0x1C4)[it->grid_index].attributes & 0x3C)
            return EXEC_DEFAULT;

      c->return_address = 0x5A849A;
      return NO_EXEC_DEFAULT;
   }
     
   return EXEC_DEFAULT;   
}

int __stdcall fixDispel(LoHook* h, HookContext* c)
{
   army* Army = (army*)c->edi;

   if (Army->active_spells_count)
   {
      stdDeque* spellDeque = (stdDeque*)(c->edi + 0x420);

      for (int i = spellDeque->size; i > 0; --i)
      {
         int spell = spellDeque->itFirst.node[i - 1];

         if (spell != SPL_POISON)
         {
            Army->CancelIndividualSpell(spell);
            break;
         }
      }
   }
         
   c->return_address = 0x5A190A;
   return NO_EXEC_DEFAULT;
}

int __stdcall fixMassDispel(LoHook* h, HookContext* c)
{
   army* Army = (army*)c->esi;

   if (Army->active_spells_count)
   {
      stdDeque* spellDeque = (stdDeque*)(c->esi + 0x420);

      for (int i = spellDeque->size; i > 0; --i)
      {
         int spell = spellDeque->itFirst.node[i - 1];

         if (spell != SPL_POISON)
         {
            Army->CancelIndividualSpell(spell);
            break;
         }
      }
   }
       
   c->return_address = 0x5A1998;
   return NO_EXEC_DEFAULT;
}

int __stdcall fixDispelHelpful(LoHook* h, HookContext* c)
{
   army* Army = (army*)c->edi;

   if (Army->active_spells_count)
   {
      stdDeque* spellDeque = (stdDeque*)(c->edi + 0x420);

      for (int i = spellDeque->size; i > 0; --i)
      {
         int spell = spellDeque->itFirst.node[i - 1];

         if (o_Spell[spell].type > 0)
         {
            Army->CancelIndividualSpell(spell);
            break;
         }
      }
   }
       
   c->return_address = 0x5A1AE2;
   return NO_EXEC_DEFAULT;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
   static bool plugin_On = false;

   if (DLL_PROCESS_ATTACH == ul_reason_for_call)
   {
      if (!plugin_On)
      {
         plugin_On = true;
         _P = GetPatcher();
         _PI = _P->CreateInstance((char*)"HD.Plugin.H3.FairCancelSpells");

         _PI->WriteLoHook(0x44627C, fixCure);
         _PI->WriteLoHook(0x5A18F7, fixDispel);
         _PI->WriteLoHook(0x5A1985, fixMassDispel);
         _PI->WriteLoHook(0x5A1A8E, fixDispelHelpful);
         _PI->WriteLoHook(0x5A8477, spellCastWorkChance);
         // TODO: fixCancelValue
      }
   }
   
   return TRUE;
}

FairCancelSpells.zip
(3.8 КБ) Скачиваний: 332
Вернуться к началу

offlinevovacyxov  
имя: Влaдимир
 
Сообщения: 9
Зарегистрирован: 08 ноя 2022, 12:30
Пол: Мужчина
Поблагодарили: 3 раз.

Re: Пользовательские плагины для HD мода

Сообщение vovacyxov » 08 ноя 2022, 12:44

Добрый день уважаемые, не могу нигде найти плагин для удаления взятого вторичного навыка. Заранее спасибо! Сам к сожалению плагины делать неумею. Буду благодарен если поможете.
Огромное Спасибо AlexSpl

скачать плагин удаление вторичных навыков можно по ссылке

viewtopic.php?f=56&t=518&start=3810
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 2185 раз.

Re: Пользовательские плагины для HD мода

Сообщение AlexSpl » 11 ноя 2022, 16:16

Авторы: AlexSpl, vovacyxov

Название: ForgetSecSkill

Описание: Вторичные навыки можно удалять прямо из окна героя. Левый клик на навыке и нажатие на кнопку Cancel приведёт к отмене этого навыка/ступени навыка.

 Код
Код: Выделить всё
#define _CRT_SECURE_NO_WARNINGS

#pragma warning(disable : 4005)
#pragma warning(disable : 4010)

#include "stdafx.h"
#include "..\..\headers\HoMM3API.h"

Patcher* _P;
PatcherInstance* _PI;

void __stdcall forgetSecSkill(HiHook* h, char const* cText, int iMBType, int x, int y,
                              int iResType1, int iResExtra1, int iResType2, int iResExtra2,
                              int iSpecial, int iTimeout, int iResType3, int iResExtra3)
{
   if (iResExtra1 && iMBType == 1)
      iMBType = 2;

   CALL_12(void, __fastcall, h->GetDefaultFunc(), cText, iMBType, x, y,
      iResType1, iResExtra1, iResType2, iResExtra2, iSpecial, iTimeout, iResType3, iResExtra3);

   if (iResExtra1 && pWindowManager->dialogReturn == 0x7806)
   {
      pWindowManager->dialogReturn = 0;

      hero* gpHero = *(hero**)0x698B70;

      if (gpHero)
      {
         int skill = (iResExtra1 - 3) / 3;
         TSkillMastery mastery = (TSkillMastery)((iResExtra1 - 3) % 3 + 1);
         
         --gpHero->SSLevel[skill];

         if (mastery == eMasteryBasic)
         {
            --gpHero->numSSs;
           
            for (int i = 0; i < 28; ++i)
               if (gpHero->SSOrder[i] > gpHero->SSOrder[skill])
                  --gpHero->SSOrder[i];

            gpHero->SSOrder[skill] = 0;
         }

         heroWindow* heroWnd = *(heroWindow**)0x698AC8;

         CALL_1(void, __thiscall, 0x4E1CC0, heroWnd);
         CALL_4(void, __thiscall, *(int*)(*(int*)heroWnd + 0x14), heroWnd, true, -65535, 65535);
      }
   }
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
   static bool plugin_On = false;

   if (DLL_PROCESS_ATTACH == ul_reason_for_call)
   {
      if (!plugin_On)
      {
         plugin_On = true;
         _P = GetPatcher();
         _PI = _P->CreateInstance((char*)"HD.Plugin.H3.ForgetSecSkill");

         _PI->WriteHiHook(0x4DE843, CALL_, EXTENDED_, FASTCALL_, forgetSecSkill);
      }
   }
   
   return TRUE;
}

ForgetSecSkill.zip
(4.52 КБ) Скачиваний: 498
Вернуться к началу

offlinejackslater777  
имя: Иван
 
Сообщения: 1
Зарегистрирован: 22 дек 2022, 12:26
Пол: Мужчина
Поблагодарили: 0 раз.

Re: Пользовательские плагины для HD мода

Сообщение jackslater777 » 23 дек 2022, 10:28

Добрый день. Ни у кого не осталось сурс-кода на плагин RandomObjectsTuning от Ben80? Буду крайне признателен за помощь.
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 2185 раз.

Re: Пользовательские плагины для HD мода

Сообщение AlexSpl » 13 июн 2023, 15:24

Автор: AlexSpl

Название: LimitMaxHeroes

Описание: Ограничивает максимальное количество героев у игроков на карте приключений. Количество героев задаётся в конфигурационном файле LimitMaxHeroes.ini, там же можно прописать сообщение для объекта "Тюрьма".

 Комментарий
Недавно один из пользователей HC задал вопрос о том, можно ли как-то ограничить количество героев у игрока на карте приключений, и я вспомнил, что у нас уже имеется такой плагин. Плагин пригодится, например, игрокам, которые любят играть одним героем или основным героем и двумя скаутами.

 Код
Код: Выделить всё
#define _CRT_SECURE_NO_WARNINGS
#include "..\..\HotA\homm3.h"

Patcher* _P;
PatcherInstance* _PI;

char iniPath[MAX_PATH];
char strBuffer[201] = "";

#define o_ADVEVENT (*(_TXTEX_**)0x696A68)

struct _TXTEX_ : public _TXT_ {
  inline void SetString(int index, char* str) { *(char**)(*(int*)((int)this + 32) + index * 4) = str; }
};

int __stdcall afterInit(LoHook* h, HookContext* c)
{
   GetPrivateProfileStringA("Messages", "Prison", o_ADVEVENT->GetString(103), strBuffer, 201, iniPath);
   o_ADVEVENT->SetString(103, strBuffer);
 
   return EXEC_DEFAULT;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
   static _bool_ plugin_On = 0;
   if ( DLL_PROCESS_ATTACH == ul_reason_for_call )
   {
       if ( !plugin_On )
       {
          plugin_On = 1;
          _P = GetPatcher();
          _PI = _P->CreateInstance("HD.Plugin.H3.LimitMaxHeroes");
         
          GetCurrentDirectoryA(sizeof(iniPath), iniPath);
          strcat(iniPath, "\\LimitMaxHeroes.ini");
         
          char maxHeroes = GetPrivateProfileIntA("Limits", "MaxHeroes", 8, iniPath);
          _PI->WriteByte(0x431391, maxHeroes);
          _PI->WriteByte(0x4A3BE1, maxHeroes);
          _PI->WriteByte(0x52A891, maxHeroes);
          _PI->WriteByte(0x5D566F, maxHeroes);
          _PI->WriteByte(0x5D7E02, maxHeroes);
          _PI->WriteByte(0x5D7E0B, maxHeroes);
          _PI->WriteByte(0x5D860A, maxHeroes);
          _PI->WriteLoHook(0x4EE1C1, afterInit);
       }
   }

  return TRUE;
}

LimitMaxHeroes.zip
(3.88 КБ) Скачиваний: 292
Вернуться к началу

offlineАватара пользователя
hippocamus  
имя: Александр
 
Сообщения: 2
Зарегистрирован: 15 янв 2024, 17:01
Пол: Мужчина
Поблагодарили: 4 раз.

Re: Пользовательские плагины для HD мода

Сообщение hippocamus » 02 фев 2024, 21:39

AlexSpl писал(а):

Авторы: AlexSpl, Ben80, igrik

Название: newDisguise

Описание: Меняет длительность и эффект заклинания "Маскировка".

С помощью диалога любой игрок может выбрать, насколько сильной армия его героя, использующего заклинание "Маскировка", будет выглядеть при просмотре этого героя игроками по правому клику. При этом информация о численности отрядов в армии героя будет искажена: преуменьшена или преувеличена.

Доступны следующие опции:

На основном и базовом уровне Магии Воздуха - 66%, 150% (от количества войск в каждом отряде).
На продвинутом - 50%, 66%, 150%, 200%.
На экспертном - 25%, 33%, 50%, 66%, 150%, 200%, 300%, 500%.

Длительность заклинания - 2 хода игрока.

 Код
Код: Выделить всё
#define _CRT_SECURE_NO_WARNINGS
#include "HotA\homm3.h"

Patcher* _P;
PatcherInstance* _PI;

static _bool_ plugin_On = 0;

_Hero_* ghostHero = new _Hero_();
_Hero_* curHero;

int nPos;

struct disguiseStruct
{
    char duration : 4;
    char index : 4;
    char unusedByte;
    short unusedWord;
} *disguise;

float getDisguiseMultiplier(int n)
{
    float multiplier[] = {1.0f, 1.5f, 2.0f, 3.0f, 5.0f};
    return n >= 0 ? multiplier[n] : 1.0f / multiplier[-n - 1];
}

void getArmyCountText(const _Hero_* hero, const int stackId, const int index = 0)
{
    sprintf(o_TextBuffer, "%s", "");
       
    if ( hero->army.type[stackId] > ID_NONE )
    {
        int armyCount = (int)(getDisguiseMultiplier(index) * hero->army.count[stackId]);
           
        // Не палимся, если выбрали модификатор, отличный от нуля
        if ( armyCount < 1 ) armyCount = 1;
           
        if ( armyCount < 10000 ) sprintf(o_TextBuffer, "%d", armyCount);
            else sprintf(o_TextBuffer, "%dk", armyCount / 1000);
    }
}

// Создаём стандартный задник с рамкой цвета игрока
void setStdBackground(_Dlg_* dlg)
{
    CALL_5(int, __thiscall, 0x48FA80, dlg, dlg->x, dlg->y, dlg->width, dlg->height);

    for (_ptr_ i = dlg->field_4C; i <= dlg->field_50; ++i)
    {
        CALL_5(int, __thiscall, 0x5FF400, dlg, 512, 13, i, o_GameMgr->GetMeID());   
    }
}

void __fastcall scrollDlgCallback(int click_id, _Dlg_* dlg)
{
    nPos = click_id - ((_DlgScroll_*)dlg->GetItem(40))->ticks_count / 2;
    dlg->GetItem(30722)->SetEnabled(nPos != 0);
    if ( nPos < 0 ) --nPos;
    float multiplier = getDisguiseMultiplier(nPos);
       
    sprintf(o_TextBuffer, "%d%s", (int)(multiplier * 100), "%");

    ((_DlgStaticText_*)dlg->GetItem(14))->SetText(o_TextBuffer);

    for (int i = 0; i < 7; ++i)
    {
        getArmyCountText(curHero, i, nPos);
        ((_DlgStaticText_*)dlg->GetItem(20 + i))->SetText(o_TextBuffer);
    }

    // Устанавливаем ползунок в ближайшее к точке клика положение
    _DlgMsg_ m = {512, 3, 40, 0, 0, 0, 0, 0};
    CALL_2(void, __thiscall, 0x5FF3A0, dlg, &m);
    dlg->Redraw(TRUE);
}

void yDlgShow(int width, int height, int count, char* captionText, char* messageText, bool middle = true, int x = 0, int y = 0)
{
    // Создаём по центру экрана пустой диалог с тенью
    _Dlg_* dlg = _CustomDlg_::Create(DLG_X_CENTER, DLG_Y_CENTER, width, height, DF_SCREENSHOT | DF_SHADOW, NULL);
       
    // Добавляем задник (фон) и рамку по цвету игрока
    setStdBackground(dlg);
           
    // Добавляем заголовок диалога
    dlg->AddItem(_DlgStaticText_::Create(0, 24, dlg->width, 30,
        captionText, "bigfont.fnt", 7, 1, ALIGN_H_CENTER | ALIGN_V_TOP, 0));

    // Добавляем текст
    dlg->AddItem(_DlgStaticText_::Create(0, 54, dlg->width, 50,
        messageText, "medfont.fnt", 1, 0, ALIGN_H_CENTER | ALIGN_V_TOP, 0));

    // Добавляем иконки армии и численность
    for (int i = 0; i < 7; ++i)
    {
        int armyTypeId = ID_NONE;
        if ( curHero->army.type[i] > ID_NONE ) armyTypeId = curHero->army.type[i] + 2;

        int leftX = (dlg->width - 236) / 2;
        // Добавляем тёмный фон для иконок
        dlg->AddItem(_DlgStaticDef_::Create(leftX + i * 34, 110, 0,
            "cprsmall.def", 0, 0, 0));
           
        // Добавляем иконки армии
        if ( armyTypeId > ID_NONE )
        {
            dlg->AddItem(_DlgStaticDef_::Create(leftX + i * 34, 110, 30 + i,
                "cprsmall.def", armyTypeId, 0, 0));
        }
           
        // Добавляем рамку для иконок
        dlg->AddItem(_DlgStaticDef_::Create(leftX + i * 34, 110, 0, "cprsmall.def", 1, 0, 0));
           
        // Добавляем надписи с численностью отрядов
        getArmyCountText(curHero, i);
        dlg->AddItem(_DlgStaticText_::Create(leftX + 1 + i * 34, 144, 32, 16,
            o_TextBuffer, "tiny.fnt", 1, 20 + i, ALIGN_H_CENTER | ALIGN_V_TOP, 0));
    }

    // Добавляем полосу горизонтальной прокрутки и устанавливаем ползунок по центру
    _DlgScroll_* disguiseScroll = _DlgScroll_::Create(30, dlg->height - 90, dlg->width - 30 * 2 + 1, 16, 40, count,
        (_ptr_)scrollDlgCallback, 0, 0, 0);
    disguiseScroll->tick = count / 2;
    disguiseScroll->tick_value = count / 2;
    disguiseScroll->btn_position = (disguiseScroll->width - disguiseScroll->btn_size2) / 2;
    dlg->AddItem(disguiseScroll);
       
    // Добавляем текстовое поле для отображения значений, выбираемых с помощью ползунка
    dlg->AddItem(_DlgStaticText_::Create((dlg->width - 72) / 2 + 1, dlg->height - 65, 72, 40,
        "100%", "medfont.fnt", 1, 14, ALIGN_H_CENTER | ALIGN_V_CENTER, 0));

    // Добавляем кнопку OK
    dlg->AddItem(_DlgStaticPcx8_::Create(30, dlg->height - 61, 0, "Box64x30.pcx"));
    dlg->AddItem(_DlgButton_::Create(31, dlg->height - 60, 64, 30, 30722, "iOkay.def", 0, 1, 1, 28, 2));
    dlg->GetItem(30722)->SetEnabled(0);

    // Добавляем кнопку Cancel
    dlg->AddItem(_DlgStaticPcx8_::Create(dlg->width - 31 - 64, dlg->height - 61, 0, "Box64x30.pcx"));
    dlg->AddItem(_DlgButton_::Create(dlg->width - 30 - 64, dlg->height - 60, 64, 30, 30721, "iCancel.def", 0, 1, 1, 1, 2));

    if ( !middle ) dlg->SetPos(x, y);
    dlg->Run();
    dlg->Destroy(TRUE);
}

_Dlg_* __stdcall makeGhostHero(HiHook* hook, _Dlg_* dlg, _Hero_* hero, int a3)
{
    disguise = (disguiseStruct*)&hero->disguise;

    if ( hero->disguise != -1 )
    {
        // Копируем героя в "призрачного" героя
        *ghostHero = *hero;
        ghostHero->disguise = -1;

        for (int i = 0; i < 7; ++i)
        {
            if ( hero->army.type[i] > ID_NONE )
            {
                ghostHero->army.count[i] = (int)(getDisguiseMultiplier(disguise->index) * ghostHero->army.count[i]);

                // Не палимся, если выбрали модификатор меньше нуля
                if ( ghostHero->army.count[i] < 1 ) ghostHero->army.count[i] = 1;
            }
        }
         
        hero = ghostHero;
    }
       
    return CALL_3(_Dlg_*, __thiscall, hook->GetDefaultFunc(), dlg, hero, a3);
}

int getNextActivePlayerID()
{
    int PlayerIDByOrder[9];
       
    int n = 0;
    for (int i = 0; i < 8; ++i)
    {
        if ( o_GameMgr->IsPlayerIngame(i) && o_GameMgr->GetPlayer(i)->IsHuman() ) PlayerIDByOrder[n++] = i;
    }

    for (int i = 0; i < 8; ++i)
    {
        if ( o_GameMgr->IsPlayerIngame(i) && !o_GameMgr->GetPlayer(i)->IsHuman() ) PlayerIDByOrder[n++] = i;
    }
       
    PlayerIDByOrder[n] = PlayerIDByOrder[0];

    int nextActivePlayerID;
    for (int i = 0; i < 8; ++i)
    {
        if ( o_ActivePlayerID == PlayerIDByOrder[i] )
        {
            nextActivePlayerID = PlayerIDByOrder[i + 1];
            break;
        }
    }

    return nextActivePlayerID;
}

int __stdcall decDisguise(LoHook* h, HookContext* c)
{
    int nextActivePlayerID = getNextActivePlayerID();

    for (int i = 0; i < o_HEROES_COUNT; ++i)
    {
        if ( o_GameMgr->GetHero(i)->owner_id == nextActivePlayerID )
        {
            disguise = (disguiseStruct*)&o_GameMgr->GetHero(i)->disguise;
            if ( disguise->duration != -1 )
            {
                if ( --disguise->duration == -1 ) o_GameMgr->GetHero(i)->disguise = -1;
            }
        }
    }

    return EXEC_DEFAULT;
}

int __stdcall setDisguise(LoHook* h, HookContext* c)
{
    nPos = 0;
    _Hero_* hero = (_Hero_*)c->esi;
       
    curHero = hero;

    // Показываем диалог с горизонтальной полосой прокрутки
    // на 3/3/5/9 значений для Основного/Базового/Продвинутого/Экспертного навыка
    sprintf(o_TextBuffer, "{%s}, select your army\nvisible strength.", hero->name);
       
    int tickCount[] = {3, 3, 5, 9};
    yDlgShow(320, 256, tickCount[hero->second_skill[HSS_AIR_MAGIC]], "{Disguise}", o_TextBuffer);
    bool disguiseCast = o_WndMgr->result_dlg_item_id == 30722;

    // Индекс множителя маскировки помещаем в старший полубайт,
    // длительность "Маскировки" (1 = 2 хода) - в младший
    if ( disguiseCast )
    {
        disguise = (disguiseStruct*)&hero->disguise;
        disguise->index = nPos;
        disguise->duration = 1;

        // Расходуем ману
        CALL_2(int, __thiscall, 0x4D9540, hero, c->eax);

        // Проигрываем звук Disguise
        CALL_1(void, __thiscall, 0x59A770, o_Spell[SPL_DISGUISE].wav_name);
        CALL_3(void, __thiscall, 0x59A7C0, -1, *(int*)0x6992A8, *(int*)0x6992AC);
    }
       
    c->return_address = 0x41C7F2;
    return NO_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.newDisguise");

            _PI->WriteCodePatch(0x4C7DA5, "%n", 3);
            _PI->WriteCodePatch(0x41C7C6, "%n", 6);
            _PI->WriteCodePatch(0x41C7BA, "%n", 5);
            _PI->WriteLoHook(0x41C7DD, setDisguise);
            _PI->WriteLoHook(0x4C6CA9, decDisguise);
            // Если герой исключается из игры, не забываем уменьшить длительность Disguise для следующего
            _PI->WriteLoHook(0x4F1865, decDisguise);
            _PI->WriteHiHook(0x52EFB0, SPLICE_, EXTENDED_, THISCALL_, makeGhostHero);
        }
    }

    return TRUE;
}

newDisguise.zip
То есть - случайным образом для каждого отряда из имеющихся для данного уровня паттернов процентов?
Для игроков это будет квест, конечно, а для ИИ ничего по сути не изменит.

Тогда как вариант - самому видеть итоговое отображение, и иметь возможность перекастовывать Скрытность до нужного результата.
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 2185 раз.

Re: Пользовательские плагины для HD мода

Сообщение AlexSpl » 03 фев 2024, 10:01

Нет, игрок сам (с помощью диалога) выбирает процент из доступных в зависимости от ситуации. С помощью Disguise можно попытаться спровоцировать нападение вражеского героя или заставить его очень сильно подумать над тем, стоит ли это делать. Конечно, если игрок уверен, что его героя ещё не спалили. Хотя, если армия одна и та же до каста (когда героя увидел вражеский герой) и после каста, вражеский игрок однозначно сказать не может, какую армию он видит сейчас: настоящую или с искажённой численностью.

Цитата:
Для игроков это будет квест, конечно, а для ИИ ничего по сути не изменит.

Я уже таких деталей не помню. Нужно было вернуть заклинание в игру, исправив баг с его продолжительностью, и хоть как-то оправдать его применение человеком. Но обмануть AI не проблема. Можно отдавать ему для анализа ghostHero вместо реального (если так уже не делается).
Вернуться к началу

offlineАватара пользователя
void_17  
имя: имя
Ветеран
Ветеран
 
Сообщения: 548
Зарегистрирован: 25 апр 2021, 15:05
Откуда: Оттуда
Пол: Мужчина
Поблагодарили: 132 раз.

Re: Пользовательские плагины для HD мода

Сообщение void_17 » 17 мар 2024, 12:50

Автор: void_17, 2021

Название: AnyDifficulty

Описание: Позволяет выбрать любую сложность в любых кампаниях.
P.S.: Нашел на жестком диске, август 2021. Наверное самый идиотский код, что я писал когда-либо. :smile12:
Ну, зато работает.

 Код
Код: Выделить всё
#include <patcher_x86.hpp>

#ifndef CREATE_LH
#define CREATE_LH(NAME) int __stdcall NAME(LoHook* h, HookContext* c)
#endif

CREATE_LH(AL_1)
{
   c->al = 1;

   return EXEC_DEFAULT;
}

CREATE_LH(Campaign_Button_Minus)
{
   c->dl += 1;
   if ( c->dl > 0 )
      --c->dl;
   else
      c->dl = 4;

   return EXEC_DEFAULT;
}

CREATE_LH(Campaign_Button_Plus)
{
   c->dl -= 1;
   if ( c->dl < 4 )
      ++c->dl;
   else
      c->dl = 0;
   
   return EXEC_DEFAULT;
}
Patcher* _P;
PatcherInstance* _PI;
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    static bool plugin_On = false;
    if ( !plugin_On )
    {
        plugin_On = true;
        _P = GetPatcher();
        _PI = _P->CreateInstance((char*)"HD.Plugin.AnyDifficulty");
      _PI->WriteLoHook(0x458D2A, AL_1);
      _PI->WriteLoHook(0x457C71, Campaign_Button_Minus);
      _PI->WriteLoHook(0x457CC1, Campaign_Button_Plus);
    }
    return TRUE;
}
Вложения
AnyDifficulty.7z
(3.99 КБ) Скачиваний: 154
Вернуться к началу

offlineАватара пользователя
void_17  
имя: имя
Ветеран
Ветеран
 
Сообщения: 548
Зарегистрирован: 25 апр 2021, 15:05
Откуда: Оттуда
Пол: Мужчина
Поблагодарили: 132 раз.

Re: Пользовательские плагины для HD мода

Сообщение void_17 » 04 апр 2024, 17:29

Автор: void_17, 2023

Название: WriteTownReqMasks

Описание: Технический плагин-пример(мне кажется нужно создать отдельный тред для такого), который демонстрирует, как можно удобно выводить данные игры, не прибегая к отладчикам. :smile1:
В данном случае он генерирует в папке игры два файлика: gHierarchyMask.txt в котором лежат маски требуемых строений для каждой постройки фракции и gTownEligibleBuildMask.txt, в котором лежат маски доступных для каждой фракции построек.

 
Код: Выделить всё
#include <patcher_x86.hpp>

#include <fstream>
#include <string_view>
#include <bitset>

using namespace std::string_view_literals;
constexpr const std::array<std::string_view, 9> townNames
{ {
    "castle"sv, "rampart"sv, "tower"sv,
    "inferno"sv, "necropolis"sv, "dungeon"sv,
    "stronghold"sv, "fortress"sv, "conflux"sv
} };

constexpr const std::array<std::string_view, 44> buildingIDs
{ {
   "MAGE_GUILD_ID"sv,
   "MAGE_GUILD2_ID"sv,
   "MAGE_GUILD3_ID"sv,
   "MAGE_GUILD4_ID"sv,
   "MAGE_GUILD5_ID"sv,
   "TAVERN_ID"sv,
   "DOCK_ID"sv,
   "CASTLE_FORT_ID"sv,
   "CASTLE_CITADEL_ID"sv,
   "CASTLE_CASTLE_ID"sv,
   "HALL_VILLAGE_ID"sv,
   "HALL_TOWN_ID"sv,
   "HALL_CITY_ID"sv,
   "HALL_CAPITOL_ID"sv,
   "MARKETPLACE_ID"sv,
   "MARKETPLACE_SILO_ID"sv,
   "BLACKSMITH_ID"sv,
   "SPECIAL_BUILDING_ID"sv,
   "HORDE_ID"sv,
   "HORDE_UPG_ID"sv,
   "DOCK_WITH_BOAT_ID"sv,
   "EXTRA_0_ID"sv,
   "EXTRA_1_ID"sv,
   "EXTRA_2_ID"sv,
   "HORDE_2_ID"sv,
   "HORDE_2_UPG_ID"sv,
   "HOLY_GRAIL_ID"sv,
   "EXTRA_3_ID"sv,
   "EXTRA_4_ID"sv,
   "EXTRA_5_ID"sv,
   "DWELLING_0_ID"sv,
   "DWELLING_1_ID"sv,
   "DWELLING_2_ID"sv,
   "DWELLING_3_ID"sv,
   "DWELLING_4_ID"sv,
   "DWELLING_5_ID"sv,
   "DWELLING_6_ID"sv,
   "DWELLING_0_UPG_ID"sv,
   "DWELLING_1_UPG_ID"sv,
   "DWELLING_2_UPG_ID"sv,
   "DWELLING_3_UPG_ID"sv,
   "DWELLING_4_UPG_ID"sv,
   "DWELLING_5_UPG_ID"sv,
   "DWELLING_6_UPG_ID"sv,
} };

void writeMasks()
{
    const int64_t* gHierarchyMask = reinterpret_cast<const int64_t*>(0x6977E8);
    const int64_t* gTownEligibleBuildMask = reinterpret_cast<const int64_t*>(0x697740);
    std::ofstream file;
    file.open("gHierarchyMask.txt");
    if ( file.is_open() )
    {
        for ( size_t i = 44 - 1; i < 396 + 44 - 1; ++i )
        {
            int modulo44 = (i + 1) % 44; // [0; 43]
            std::bitset<64> currBits(*gHierarchyMask);
            if ( modulo44 == 0 )
                file << '\n' << townNames[((i + 1) / 44) - 1] << '\n';
            file << currBits << ' ' << buildingIDs[modulo44] << '\n';
            ++gHierarchyMask;
        }
    }
    file.close();
    file.open("gTownEligibleBuildMask.txt");
    if ( file.is_open() )
    {
        for ( size_t i = 0; i < 9; ++i )
        {
            std::bitset<64> currBits(*gTownEligibleBuildMask);
            file << townNames[i] << '\n';
            file << currBits << '\n';
            ++gTownEligibleBuildMask;
        }
    }
    file.close();
}

// 0x4EB810
void __stdcall HH_initialize_hordes(HiHook* h)
{
    writeMasks();
    CALL_0(void, __fastcall, h->GetDefaultFunc());
}

Patcher* _P;
PatcherInstance* _PI;
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    static bool plugin_On = false;
    if ( !plugin_On )
    {
        plugin_On = true;
        _P = GetPatcher();
        _PI = _P->CreateInstance((char*)"HD.Plugin.Example.WriteMasks");
        _PI->WriteHiHook(0x5BE360, SPLICE_, EXTENDED_, FASTCALL_, HH_initialize_hordes);
    }
    return TRUE;
}
Вложения
WriteMasks.7z
(79.29 КБ) Скачиваний: 133
gTownEligibleBuildMask.txt
(679 байт) Скачиваний: 143
gHierarchyMask.txt
(31.24 КБ) Скачиваний: 135
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 2185 раз.

Re: Пользовательские плагины для HD мода

Сообщение AlexSpl » 11 апр 2024, 14:19

Автор: AlexSpl

Название: generatorPlus

Описание: Обеспечивает накопление существ во внешних жилищах после их захвата.

 Код
Код: Выделить всё
#include "stdafx.h"
#include "..\..\HotA\patcher_x86.hpp"

Patcher* _P;
PatcherInstance* _PI;

int __stdcall testOwnership(LoHook* h, HookContext* c)
{
    short population = *(short*)c->edi;
    char playerOwner = *(char*)(*(int*)(c->ebp - 8) + 0x3B);

    if (playerOwner != -1)
        c->ecx += population;
   
    return EXEC_DEFAULT;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    static bool pluginOn = false;

    if ( DLL_PROCESS_ATTACH == ul_reason_for_call )
    {
        if ( !pluginOn )
        {
            pluginOn = true;
            _P = GetPatcher();
            _PI = _P->CreateInstance((char*)"HD.Plugin.GeneratorPlus");
            _PI->WriteLoHook(0x4B87A0, testOwnership);
        }
    }

    return TRUE;
}

generatorPlus.zip
(3.34 КБ) Скачиваний: 180
Вернуться к началу

Пред.

Вернуться в Общий раздел

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 14