Объявления
Поздравляем
Roman2211


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

Как создать плагин для HD мода

Герои Меча и Магии III: Возрождение Эрафии, Герои Меча и Магии III Дыхание Смерти, Герои Меча и Магии III Клинок Армагеддона, Герои Меча и Магии III Хроники Героев
offlineRoseKavalier  
Мастер
Мастер
 
Сообщения: 331
Зарегистрирован: 23 сен 2017, 17:00
Пол: Не указан
Поблагодарили: 234 раз.

Re: Как создать плагин для HD мода

Сообщение RoseKavalier » 02 мар 2021, 13:35

AlexSpl писал(а):

You can break one plugin with another in more vague ways, so let's just call it incompatibility :smile2:

Цитата:
All adventure map objects , including Scholar.

Why do you use unsigned type for bit fields when they are actually signed (shl, sar)?


I just assumed everything as unsigned to save time, it's a mix of both depending on the object and the field.
In most cases it makes no difference because the maximum value is well below half of the capacity... but yes, would have to be corrected to be spot.
Вернуться к началу

offlineRolex  
имя: Alex
Ветеран
Ветеран
 
Сообщения: 898
Зарегистрирован: 22 сен 2020, 18:58
Откуда: УКРАИНА
Пол: Мужчина
Поблагодарили: 53 раз.

Re: Как создать плагин для HD мода

Сообщение Rolex » 03 мар 2021, 10:08

AlexSpl, подскажите, пожалуйста, насколько сложно будет вычислить devCoef[] для HD+?
Вернуться к началу

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 мар 2021, 11:59

RoseKavalier же поделился функцией, которая автоматически определяет HD/HD+ и разбивает отряд соответственно.
Вернуться к началу

offlineRolex  
имя: Alex
Ветеран
Ветеран
 
Сообщения: 898
Зарегистрирован: 22 сен 2020, 18:58
Откуда: УКРАИНА
Пол: Мужчина
Поблагодарили: 53 раз.

Re: Как создать плагин для HD мода

Сообщение Rolex » 03 мар 2021, 15:16

Цитата:
RoseKavalier же поделился функцией, которая автоматически определяет HD/HD+ и разбивает отряд соответственно.

Там же используется H3API. У меня весь код по охране существ написан без использования H3API. Исходя из вашего сообщения ваша функция getStacksCount с Deviation будет работать и для HD+, только лишь нужно вычислить коеф судя из вашего ответа выше. Просто если я сейчас начну городить ту здоровую функция на H3API и править весь код, я еще больше запутаюсь. Нужно всего лишь вычислить 4 коеф для HD+.

А по определению HD/HD+ у меня такая идея:

1) Получаем в строковую переменную путь к плагину - Папка_с_игрой\_HD3_Data\Packs\Папка_с_плагином:

2) Убираем из полученного строки \Packs\Папка_с_плагином

3) Дописываем к полученной строке \Settings\sod.ini

4) Ищем в данном файле строку <HD+>

5) Если <HD+> = 0, то берем коеф для HD, а если 1, то для HD+.
Последний раз редактировалось Rolex 05 мар 2021, 18:26, всего редактировалось 6 раз(а).
Вернуться к началу

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 мар 2021, 15:20

Вы можете просто взять код оттуда. Идея там такая: вызывается функция, которая считает seed. Если она подменяется HD+, то вызывается та, которой подменяется. Попробуйте просто перенести текст функции и те, от которых она зависит (если не получится, я сам позже посмотрю). А коэффициенты посчитать не так просто, как может показаться. Плюс я не уверен, что функция HD+ использует только координаты объекта.
Вернуться к началу

offlineRolex  
имя: Alex
Ветеран
Ветеран
 
Сообщения: 898
Зарегистрирован: 22 сен 2020, 18:58
Откуда: УКРАИНА
Пол: Мужчина
Поблагодарили: 53 раз.

Re: Как создать плагин для HD мода

Сообщение Rolex » 03 мар 2021, 16:12

Ясно. Ну тогда публикую весь код по охране, который у меня получился. Надеюсь, вы поможете его доработать. Как я понял нам нужно будет заменить вашу функцию getStacksCount на эту.

Плюс у нас же еще есть upgradedStack и по идеи она и ее должна заменить. В обе функции вы передаете коеф дабл k и 3 чара x, y, z. Там же другие аргументы принимаються какая-то константа, указатель на героя, и два инта - тип и сумма. Я использую H3API.hpp - single header.


Код: Выделить всё
#define _CRT_SECURE_NO_WARNINGS
#include "stdafx.h"
#include "HotA\homm3.h"
#include <string>
#include "H3API\single_header\H3API.hpp"

using namespace std;
using namespace h3;

Patcher* _P;
PatcherInstance* _PI;
static _bool_ plugin_On = 0;

#define o_CreatureInfo ((_CreatureInfo_*)0x6703B8)

string getInterval(int n)
{
   string str;
   if (n < 1) str = "0"; else
      if (n < 5) str = "1-4"; else
         if (n < 10) str = "5-9"; else
            if (n < 20) str = "10-19"; else
               if (n < 50) str = "20-49"; else
                  if (n < 100) str = "50-99"; else
                     if (n < 250) str = "100-249"; else
                        if (n < 500) str = "250-499"; else
                           if (n < 1000) str = "500-999"; else str = "1000+";
   return str;
}

_MapItem_* mapItem;
int __stdcall saveObjEntranceAddr(LoHook* h, HookContext* c)
{
   mapItem = (_MapItem_*)c->eax;

   return EXEC_DEFAULT;
}

// Получаем указатель на _Dwelling_ по ID жилища существ
_Dwelling_* GetDwelling(_int_ dwellingId) {
   return (_Dwelling_ *)(o_GameMgr->Field<int>(0x4E39C) + 92 * dwellingId);
}

int __stdcall Crypt_SkipMsgIfVisited(LoHook* h, HookContext* c)
{
   if (*(int*)*(int*)(c->ebp + 0xC) & 0x2000000)
   {
      c->return_address = 0x4AC1E5;
      return NO_EXEC_DEFAULT;
   }

   return EXEC_DEFAULT;
}

int __stdcall Pyramid_SkipMsgIfVisited(LoHook* h, HookContext* c)
{
   // Получаем состояние пирамиды
   _MapItem_* mapItem = (_MapItem_*)*(int*)(c->ebp + 0xC);

   if (mapItem->setup & 1) return EXEC_DEFAULT;

   c->return_address = 0x4A3F22;
   return NO_EXEC_DEFAULT;
}

int getStacksCount(double k, char x, char y, char z)
{
   __int64 devCoef[] = { 0x5C6F80EB, 0xC8374AB9, 0x73D409E3, 0xBD38DECE };

   int n = 2;
   if (k < 0.50) n = 7; else
      if (k < 0.67) n = 6; else
         if (k < 1.00) n = 5; else
            if (k < 1.50) n = 4; else
               if (k < 2.00) n = 3;


   // Deviation
   __int64 splitValue = (((devCoef[0] * x + devCoef[1] * y + devCoef[2] * z + devCoef[3]) >> 0x10) & 0x7FFF) % 100 + 1;
   if (splitValue <= 20) --n;
   if (splitValue >= 80 && n < 7) ++n;

   return n;
}

bool upgradedStack(double k, char x, char y, char z)
{
   __int64 upgCoef[] = { 0x0BB0E93F, 0x375E43D5, 0x14CD2E57, 0x8014BA59 };

   return (((upgCoef[0] * x + upgCoef[1] * y + upgCoef[2] * z + upgCoef[3]) >> 0x10) & 0x7FFF) % 100 < 50;
}

int __stdcall showGuards(LoHook* h, HookContext* c)
{
   // Получаем состояние банка
   _MapItem_* mapItem = (_MapItem_*)*(int*)(c->ebp + 0xC);

   if (mapItem) // Перестраховываемся, но можно и без этой проверки
   {
      _CrBankState_* bankState = CALL_1(_CrBankState_*, __fastcall, 0x405D80, mapItem);

      string str = (char*)c->ecx; // Оригинальное сообщение
      str += "\n";

      for (int i = 0; i < bankState->defenders.GetStacksCount(); ++i)
         str = str + "\n" + o_CreatureInfo[bankState->defenders.type[i]].name_plural + ": " +
         getInterval(bankState->defenders.count[i]);

      // Передаём адрес текстового буфера в качестве аргумента для диалога
      sprintf(o_TextBuffer, "%s", str.c_str());
      c->ecx = (int)o_TextBuffer;

      mapItem->SetAsVisited(o_GameMgr->GetMeID());
   }

   return EXEC_DEFAULT;
}

int __stdcall showMonsters(LoHook* h, HookContext* c)
{
   // Получаем состояние жилища
   _MapItem_* mapItem = (_MapItem_*)*(int*)(c->ebp + 0xC);

   if (mapItem) // Перестраховываемся, но можно и без этой проверки
   {
      string str = (char*)c->ecx; // Оригинальное сообщение
      str += "\n";

      // Получаем состояние жилища существ
      _Dwelling_* dwelling = GetDwelling(mapItem->setup);

      // Если мы владельцы, показываем количество существ
      if (dwelling && dwelling->owner_ix == o_GameMgr->GetMeID())
      {
         string str = (char*)c->ecx; // Оригинальное сообщение
         str += "\n\n";

         str += "Доступные существа:";

         for (int i = 0; i < 4; ++i)
            if (dwelling->creature_types[i] != ID_NONE)
               str = str + "\n" + o_CreatureInfo[dwelling->creature_types[i]].name_plural + " — " + "{" +
               to_string(dwelling->creature_counts[i]) + "}";

         // Передаём адрес текстового буфера в качестве аргумента для диалога
         sprintf(o_TextBuffer, "%s", str.c_str());
         c->ecx = (int)o_TextBuffer;
      }
   }

   return EXEC_DEFAULT;
}

int __stdcall showAbandonedMine(LoHook* h, HookContext* c)
{
   // Получаем состояние шахты
   _MapItem_* mapItem = (_MapItem_*)*(int*)(c->ebp + 8);

   // Если есть охрана
   if (*(char*)(o_GameMgr->Field<int>(0x4E38C) + (mapItem->setup << 6)) < 0)
   {
      // Если герой выбран
      if (o_ActivePlayer->selected_hero_id > ID_NONE && o_ActivePlayer->selected_hero_id < 156)
      {
         _Hero_* hero = o_GameMgr->GetHero(o_ActivePlayer->selected_hero_id);
         int heroArmyPower = CALL_1(int, __fastcall, 0x44A950, &hero->army);

         _Army_* defArmy = (_Army_*)(o_GameMgr->Field<int>(0x4E38C) + (mapItem->setup << 6) + 4);
         int defArmyPower = CALL_1(int, __fastcall, 0x44A950, defArmy);

         if (defArmyPower) // На всякий (чтобы избежать неожиданного деления на 0)
         {
            double k = (double)heroArmyPower / defArmyPower;

            // Вычисляем координаты входа
            int mapSize = o_AdvMgr->map->size;
            int pos = mapItem - o_AdvMgr->map->items;
            char x = pos % mapSize;
            pos /= mapSize;
            char y = pos % mapSize;
            char z = pos / mapSize;

            // Получаем количество стеков, на которое разбилась охрана
            int stackCount = getStacksCount(k, x, y, z);

            // Выясняем, есть ли улучшенный стек
            char* upgStr = upgradedStack(k, x, y, z) ? "Yes" : "No";

            string str = (char*)c->ecx; // Оригинальное сообщение
            str += "\n";

            int position = 0;

            for (int i = 0; i < defArmy->count[0] % stackCount; ++i)
            {
               position++;
               if (upgStr == "Yes" && position == stackCount / 2 + 1) str = str + "\n" + o_CreatureInfo[CALL_1(int, __fastcall, 0x47AAD0, defArmy->type[0])].name_plural + ": "; else
                  str = str + "\n" + o_CreatureInfo[defArmy->type[0]].name_plural + ": ";

               str = str + to_string(defArmy->count[0] / stackCount + 1);
            }


            for (int i = 0; i < stackCount - (defArmy->count[0] % stackCount); ++i)
            {
               position++;
               if (upgStr == "Yes" && position == stackCount / 2 + 1) str = str + "\n" + o_CreatureInfo[CALL_1(int, __fastcall, 0x47AAD0, defArmy->type[0])].name_plural + ": "; else
                  str = str + "\n" + o_CreatureInfo[defArmy->type[0]].name_plural + ": ";

               str = str + to_string(defArmy->count[0] / stackCount);
            }


            // Передаём адрес текстового буфера в качестве аргумента для диалога
            sprintf(o_TextBuffer, "%s", str.c_str());


            c->ecx = (int)o_TextBuffer;
         }
      }
   }

   return EXEC_DEFAULT;
}

int __stdcall showPyramid(LoHook* h, HookContext* c)
{
   // Получаем состояние пирамиды
   _MapItem_* mapItem = (_MapItem_*)*(int*)(c->ebp + 0xC);

   // Если есть охрана
   if (mapItem->setup & 1)
   {
      // Если герой выбран
      if (o_ActivePlayer->selected_hero_id > ID_NONE && o_ActivePlayer->selected_hero_id < 156)
      {
         _Hero_* hero = o_GameMgr->GetHero(o_ActivePlayer->selected_hero_id);
         int heroArmyPower = CALL_1(int, __fastcall, 0x44A950, &hero->army);

         int defArmyPower = 20 * o_CreatureInfo[CID_DIAMOND_GOLEM].AI_value + 40 * o_CreatureInfo[CID_GOLD_GOLEM].AI_value;

         if (defArmyPower) // На всякий (чтобы избежать неожиданного деления на 0)
         {
            double k = (double)heroArmyPower / defArmyPower;

            // Вычисляем координаты входа
            int mapSize = o_AdvMgr->map->size;
            int pos = mapItem - o_AdvMgr->map->items;
            char x = pos % mapSize;
            pos /= mapSize;
            char y = pos % mapSize;
            char z = pos / mapSize;

            // Получаем количество стеков, на которое разбилась охрана
            int stackCount = getStacksCount(k, x, y, z);

            stackCount -= 2;
            if (stackCount < 1) stackCount = 1;

            // Передаём адрес текстового буфера в качестве аргумента для диалога
            sprintf(o_TextBuffer, "%s\n\n%s: %d\n%s: %d\n\n# of stacks of %s: %d", (char*)c->ecx, o_CreatureInfo[CID_DIAMOND_GOLEM].name_plural, 20,
               o_CreatureInfo[CID_GOLD_GOLEM].name_plural, 40, o_CreatureInfo[CID_GOLD_GOLEM].name_plural, stackCount);
            c->ecx = (int)o_TextBuffer;
         }
      }
   }

   mapItem->SetAsVisited(o_GameMgr->GetMeID());

   return EXEC_DEFAULT;
}

int __stdcall showRefugeeCamp(LoHook* h, HookContext* c)
{
   // Получаем состояние жилища
   _MapItem_* mapItem = (_MapItem_*)*(int*)(c->ebp + 0xC);

   if (mapItem) // Перестраховываемся, но можно и без этой проверки
   {
      string str = (char*)c->ecx; // Оригинальное сообщение

                           // Передаём адрес текстового буфера в качестве аргумента для диалога
      sprintf(o_TextBuffer, "%s\n\nДоступные существа:\n%s — {%d}", str.c_str(), o_CreatureInfo[mapItem->os_type].name_plural, mapItem->setup);
      c->ecx = (int)o_TextBuffer;
   }

   return EXEC_DEFAULT;
}

int __stdcall showGuardsRMB(LoHook* h, HookContext* c)
{

   // Пирамида
   if (mapItem->object_type == 0x3F)
   {
      // Если есть охрана
      if (mapItem->setup & 1)
      {
         // Если герой выбран
         if (o_ActivePlayer->selected_hero_id > ID_NONE && o_ActivePlayer->selected_hero_id < 156)
         {
            _Hero_* hero = o_GameMgr->GetHero(o_ActivePlayer->selected_hero_id);
            int heroArmyPower = CALL_1(int, __fastcall, 0x44A950, &hero->army);

            int defArmyPower = 20 * o_CreatureInfo[CID_DIAMOND_GOLEM].AI_value + 40 * o_CreatureInfo[CID_GOLD_GOLEM].AI_value;

            if (defArmyPower) // На всякий (чтобы избежать неожиданного деления на 0)
            {
               double k = (double)heroArmyPower / defArmyPower;

               // Вычисляем координаты входа
               int mapSize = o_AdvMgr->map->size;
               int pos = mapItem - o_AdvMgr->map->items;
               char x = pos % mapSize;
               pos /= mapSize;
               char y = pos % mapSize;
               char z = pos / mapSize;

               // Получаем количество стеков, на которое разбилась охрана
               int stackCount = getStacksCount(k, x, y, z);

               stackCount -= 2;
               if (stackCount < 1) stackCount = 1;

               // Передаём адрес текстового буфера в качестве аргумента для диалога
            //   sprintf(o_TextBuffer, "%s\n\n%s: %d\n%s: %d\n\n# of stacks of %s: %d", (char*)c->ecx, o_CreatureInfo[CID_DIAMOND_GOLEM].name_plural, 20,
            //      o_CreatureInfo[CID_GOLD_GOLEM].name_plural, 40, o_CreatureInfo[CID_GOLD_GOLEM].name_plural, stackCount);
            //   c->ecx = (int)o_TextBuffer;

               string str = (char*)c->ecx; // Оригинальное сообщение
               str += "\n";

               str = str + "\n" + to_string(stackCount);
               str = str + "\n" + to_string(k);
               
               sprintf(o_TextBuffer, "%s", str.c_str());
               c->ecx = (int)o_TextBuffer;
            }
         }
      }
   }

   if (mapItem)
   {
      /*   // Банки существ
      if (mapItem->IsVisited(o_GameMgr->GetMeID()))
      {
      // mapItem->object_type = 0x10 - стандартные банки, 0x18 - Derelict Ship, 0x19 - Dragon Utopia, 0x54 - Crypt, 0x55 - Shipwreck
      int objID = mapItem->object_type;

      if (mapItem->object_type == 0x10 || mapItem->object_type == 0x18 || mapItem->object_type == 0x19 ||
      mapItem->object_type == 0x54 || mapItem->object_type == 0x55)
      {
      // Получаем состояние банка
      _CrBankState_* bankState = CALL_1(_CrBankState_*, __fastcall, 0x405D80, mapItem);

      string str = (char*)c->ecx; // Оригинальное сообщение
      str += "\n";

      for (int i = 0; i < bankState->defenders.GetStacksCount(); ++i)
      str = str + "\n" + o_CreatureInfo[bankState->defenders.type[i]].name_plural + ": " +
      getInterval(bankState->defenders.count[i]);

      // Передаём адрес текстового буфера в качестве аргумента для диалога
      sprintf(o_TextBuffer, "%s", str.c_str());
      c->ecx = (int)o_TextBuffer;
      }
      }
      */



      // Жилища существ
      if (mapItem->object_type == 0x11 || mapItem->object_type == 0x14)
      {
         // Получаем состояние жилища существ
         _Dwelling_* dwelling = GetDwelling(mapItem->setup);

         // Если мы владельцы, показываем количество существ
         if (dwelling && dwelling->owner_ix == o_GameMgr->GetMeID())
         {
            string str = (char*)c->ecx; // Оригинальное сообщение
            str += "\n\n";

            str += "Доступные существа:";

            for (int i = 0; i < 4; ++i)
               if (dwelling->creature_types[i] != ID_NONE)
                  str = str + "\n" + o_CreatureInfo[dwelling->creature_types[i]].name_plural + " — " + "{" +
                  to_string(dwelling->creature_counts[i]) + "}";

            // Передаём адрес текстового буфера в качестве аргумента для диалога
            sprintf(o_TextBuffer, "%s", str.c_str());
            c->ecx = (int)o_TextBuffer;
         }
      }

      // Лагерь беженцев
      if (mapItem->object_type == 0x4E)
      {
         string str = (char*)c->ecx; // Оригинальное сообщение

                              // Передаём адрес текстового буфера в качестве аргумента для диалога
         sprintf(o_TextBuffer, "%s\n\nДоступные существа:\n%s — {%d}", str.c_str(), o_CreatureInfo[mapItem->os_type].name_plural, mapItem->setup);
         c->ecx = (int)o_TextBuffer;
      }

      // Заброшенная шахта
      if (mapItem->object_type == 0x35 && mapItem->os_type == 7)
      {
         // Если есть охрана
         if (*(char*)(o_GameMgr->Field<int>(0x4E38C) + (mapItem->setup << 6)) < 0)
         {
            // Если герой выбран
            if (o_ActivePlayer->selected_hero_id > ID_NONE && o_ActivePlayer->selected_hero_id < 156)
            {
               _Hero_* hero = o_GameMgr->GetHero(o_ActivePlayer->selected_hero_id);
               int heroArmyPower = CALL_1(int, __fastcall, 0x44A950, &hero->army);

               _Army_* defArmy = (_Army_*)(o_GameMgr->Field<int>(0x4E38C) + (mapItem->setup << 6) + 4);
               int defArmyPower = CALL_1(int, __fastcall, 0x44A950, defArmy);

               if (defArmyPower) // На всякий (чтобы избежать неожиданного деления на 0)
               {
                  double k = (double)heroArmyPower / defArmyPower;

                  // Вычисляем координаты входа (можно через PosMixed, но пусть будет так)               
                  int mapSize = o_AdvMgr->map->size;
                  int pos = ((int)mapItem - (int)o_AdvMgr->map->items) / sizeof(_MapItem_);
                  char z = pos >= mapSize * mapSize;
                  char x = pos % mapSize;
                  char y = (pos - z * mapSize * mapSize) / mapSize;

                  // Получаем количество стеков, на которое разбилась охрана
                  int stackCount = getStacksCount(k, x, y, z);

                  // Выясняем, есть ли улучшенный стек
                  char* upgStr = upgradedStack(k, x, y, z) ? "Yes" : "No";

                  
                  //**********************
                  
                  // Передаём адрес текстового буфера в качестве аргумента для диалога
                  //   sprintf(o_TextBuffer, "%s\n\n%s: %d\n\n%s%d\n\n%s%s", (char*)c->ecx, o_CreatureInfo[defArmy->type[0]].name_plural, defArmy->count[0],
                 //      "# of stacks: ", getStacksCount(k, x, y, z), "Upgraded stack: ", upgStr);

                  //**********************
                  
                  string str = (char*)c->ecx; // Оригинальное сообщение
                  str += "\n";
                  
                  int position = 0;

                  for (int i = 0; i < defArmy->count[0] % stackCount; ++i)
                  {
                     position++;
                     if (upgStr == "Yes" && position == stackCount / 2 + 1) str = str + "\n" + o_CreatureInfo[CALL_1(int, __fastcall, 0x47AAD0, defArmy->type[0])].name_plural + ": "; else
                        str = str + "\n" + o_CreatureInfo[defArmy->type[0]].name_plural + ": ";
                        
                         str = str + to_string(defArmy->count[0] / stackCount + 1);
                  }


                  for (int i = 0; i < stackCount - (defArmy->count[0] % stackCount); ++i)
                  {
                     position++;
                     if (upgStr == "Yes" && position == stackCount / 2 + 1) str = str + "\n" + o_CreatureInfo[CALL_1(int, __fastcall, 0x47AAD0, defArmy->type[0])].name_plural + ": "; else
                        str = str + "\n" + o_CreatureInfo[defArmy->type[0]].name_plural + ": ";
                        
                         str = str + to_string(defArmy->count[0] / stackCount);
                  }
                  

                  // Передаём адрес текстового буфера в качестве аргумента для диалога
                  sprintf(o_TextBuffer, "%s", str.c_str());
                  

                  c->ecx = (int)o_TextBuffer;
               }
            }
         }
      }
   }

   return EXEC_DEFAULT;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
   if (DLL_PROCESS_ATTACH == ul_reason_for_call)
   {
      if (!plugin_On)
      {
         plugin_On = 1;
         _P = GetPatcher();
         _PI = _P->CreateInstance((char*)"HD.Plugin.ShowGuards");

         // Сообщения по правому клику
         _PI->WriteLoHook(0x415AEE, showGuardsRMB);

         // Сообщения при посещении
         _PI->WriteLoHook(0x4A13E6, showGuards); // стандартных банков
         _PI->WriteLoHook(0x4A1E56, showGuards); // Dragon Utopia
         _PI->WriteLoHook(0x4AC1B9, showGuards); // Crypt, Derelict Ship, Shipwreck
         _PI->WriteLoHook(0x4A18FF, showMonsters); // жилища существ 1-го уровня
         _PI->WriteLoHook(0x4A36E4, showAbandonedMine); // Abandoned Mine
         _PI->WriteLoHook(0x4A3EF5, showPyramid); // Pyramid
         _PI->WriteLoHook(0x4A435C, showRefugeeCamp); // Refugee Camp

         _PI->WriteLoHook(0x413917, saveObjEntranceAddr); // Сохраняем адрес тайла-входа в объект
         _PI->WriteLoHook(0x4AC19D, Crypt_SkipMsgIfVisited); // убираем диалог обыска при посещении Склепа и Кораблей, когда они уже разграблены                                                
         _PI->WriteLoHook(0x4A3ED3, Pyramid_SkipMsgIfVisited); // Пропускаем сообщения для Пирамиды, если она посещена
      }
   }

   return TRUE;
}
Вернуться к началу

offlineRoseKavalier  
Мастер
Мастер
 
Сообщения: 331
Зарегистрирован: 23 сен 2017, 17:00
Пол: Не указан
Поблагодарили: 234 раз.

Re: Как создать плагин для HD мода

Сообщение RoseKavalier » 03 мар 2021, 18:43

Rolex писал(а):

Цитата:
RoseKavalier же поделился функцией, которая автоматически определяет HD/HD+ и разбивает отряд соответственно.

Там же используется H3API. У меня весь код по охране существ написан без использования H3API. Исходя из вашего сообщения ваша функция getStacksCount с Deviation будет работать и для HD+, только лишь нужно вычислить коеф судя из вашего ответа выше. Просто если я сейчас начну городить ту здоровую функция на H3API и править весь код, я еще больше запутаюсь. Нужно всего лишь вычислить 4 коеф для HD+.

А по определению HD/HD+ у меня таккая идея:

1) Получаем в строковую переменную путь к плагину - Папка_с_игрой\_HD3_Data\Packs\Папка_с_плагином:

2) Убираем из полученного строки \Packs\Папка_с_плагином

3) Дописываем к полученной строке \Settings\sod.ini

4) Ищем в данном файле строку <HD+>

5) Если <HD+> = 0, то берем коеф для HD, а если 1, то для HD+.


Код: Выделить всё
// HD+ variable is created if the option is on. Set default value to false in case the variable does not exist.
bool hd_plus = _P->VarGetValue("HD+", false);
Вернуться к началу

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 » 04 мар 2021, 10:47

Разбивка на отряды и грейженый стек с автоопределением HD+ (на основе кода RoseKavalier):

Код: Выделить всё
bool upgradedStack(char x, char y, char z)
{
    unsigned seed = 0x3C907 * x + 0x4386D * y + 0x4BB5F * z + 0x25EA7;
    CALL_1(void, __thiscall, 0x4AC2DB + *(int*)0x4AC2D7, seed);
    // Выполняем бросок для алгоритма деления на отряды,
    // т.к. в игре деление на отряды и определение грейда происходят последовательно;
    // Мы же разбили это на две функции
    Randint(1, 100);

    return Randint(1, 100) <= 50;
}

int getStacksCount(double k, char x, char y, char z)
{
    int n = 2;
    if ( k < 0.50 ) n = 7; else
    if ( k < 0.67 ) n = 6; else
    if ( k < 1.00 ) n = 5; else
    if ( k < 1.50 ) n = 4; else
    if ( k < 2.00 ) n = 3;

    unsigned seed = 0x3C907 * x + 0x4386D * y + 0x4BB5F * z + 0x25EA7;
    CALL_1(void, __thiscall, 0x4AC2DB + *(int*)0x4AC2D7, seed);

    int splitValue = Randint(1, 100);
    if ( splitValue <= 20 ) --n;
    if ( splitValue >= 80 && n < 7 ) ++n;

    return n;
}

Примечания.

Случаи, когда число существ меньше, чем число отрядов, на которое они должны поделиться, а также когда грейд есть, но существа уже грейженые или не имеют грейда, рассматривайте самостоятельно.

В этой версии seed после вызовов Randint() не восстанавливается. Если Вы не знаете, зачем его восстанавливать, то, значит, это лишнее.
Вернуться к началу

offlineRolex  
имя: Alex
Ветеран
Ветеран
 
Сообщения: 898
Зарегистрирован: 22 сен 2020, 18:58
Откуда: УКРАИНА
Пол: Мужчина
Поблагодарили: 53 раз.

Re: Как создать плагин для HD мода

Сообщение Rolex » 04 мар 2021, 13:06

AlexSpl писал(а):

В этой версии seed после вызовов Randint() не восстанавливается. Если Вы не знаете, зачем его восстанавливать, то, значит, это лишнее.

Наверное, вам тогда и не стоило об этом и говорить. :smile1: Раз уже затронули эту тему, тогда было бы интересно услышать ответ на вопрос: а зачем его восстанавливать? И как это сделать? :smile2:
Последний раз редактировалось Rolex 04 мар 2021, 13:14, всего редактировалось 2 раз(а).
Вернуться к началу

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 » 04 мар 2021, 13:07

Если Вы не играете оффлайн-турниры, то без разницы: восстанавливать или нет. Для оффлайна восстановление seed важно для повторяемости некоторых событий.
Вернуться к началу

Пред.След.

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

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

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