Объявления

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

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

Герои Меча и Магии III: Возрождение Эрафии, Герои Меча и Магии III Дыхание Смерти, Герои Меча и Магии III Клинок Армагеддона, Герои Меча и Магии III Хроники Героев
offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5547
Зарегистрирован: 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)
Поблагодарили: 2161 раз.

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

Сообщение AlexSpl » 02 ноя 2020, 19:51

Попробуйте:

Код: Выделить всё
_BattleStack_* stack = /* получаем указатель на отряд */;
int shotsLeft = stack->creature.shots;
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5547
Зарегистрирован: 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)
Поблагодарили: 2161 раз.

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

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

Всё оказалось проще, чем я думал. __int64 возвращается в паре eax:edx (проверил в дебаггере). Заметил, что PlayWAVFile() хукнутая из HD_SOD: идёт вызов LoadWAVplayAsync(), но это внутренние дела HD мода. Мне важно, что я отдаю этой функции то, что она просит, и получаю на выходе то, что прошу я. Вот что получилось в итоге:

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

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

#define LODWORD(qword) ((DWORD)(qword))
#define HIDWORD(qword) ((DWORD)(((qword) >> 32) & 0xFFFFFFFF))

struct HealingInfo {
    int stackID;
    int removedDamage;
} healingInfo[21 + 1];

int __stdcall afterInit(LoHook* h, HookContext* c)
{
    o_pCreatureInfo[CID_FIRST_AID_TENT].hit_points = 300;
    o_pCreatureInfo[CID_FIRST_AID_TENT].defence = 10;
    o_pCreatureInfo[CID_FIRST_AID_TENT].cost.gold = 1500;

    // Здесь можно также прописать все изменения текстовиков
   
    return EXEC_DEFAULT;
}

int __stdcall findTargetsToHeal(LoHook* h, HookContext* c)
{
    _Hero_* hero = o_BattleMgr->hero[o_BattleMgr->current_side];

    if ( hero && hero->second_skill[HSS_FIRST_AID] )
    {
        int n = 0;
        for (int i = 0; i < o_BattleMgr->stacks_count[o_BattleMgr->current_side]; ++i)
        {
            _BattleStack_* stack = &o_BattleMgr->stack[o_BattleMgr->current_side][i];
       
            bool isSubjectToBeHealed = false;
           
            if ( stack->lost_hp ) {
                switch ( hero->second_skill[HSS_FIRST_AID] ) {
                    case 1: isSubjectToBeHealed = !(stack->creature.flags & 0x200040); break;
                    case 2: isSubjectToBeHealed = !(stack->creature.flags & 0x200000) && stack->creature_id != CID_FIRST_AID_TENT; break;
                    case 3: isSubjectToBeHealed = !(stack->creature.flags & 0x200000); break;
                }
            }

            if ( isSubjectToBeHealed )
            {
                healingInfo[n++].stackID = i;

                // Даём алгоритму игры валидную цель для хила
                *(int*)(c->ebp - 8) = i;
            }
        }
   
        healingInfo[n].stackID = -1;

        c->return_address = healingInfo[0].stackID < 0 ? 0x4738B3 : 0x4738C3;
        return NO_EXEC_DEFAULT;
    }

    return EXEC_DEFAULT;
}

int __stdcall applyHealing(LoHook* h, HookContext* c)
{
    _Hero_* hero = o_BattleMgr->hero[o_BattleMgr->current_side];

    if ( hero && hero->second_skill[HSS_FIRST_AID] )
    {
        int i = 0;
        while ( healingInfo[i].stackID != -1 )
        {
            _BattleStack_* stack = &o_BattleMgr->stack[o_BattleMgr->current_side][healingInfo[i].stackID];
           
            if ( c->eax < stack->lost_hp ) {
                healingInfo[i].removedDamage = c->eax;
                stack->lost_hp -= c->eax;
            }
            else {
                healingInfo[i].removedDamage = stack->lost_hp;
                stack->lost_hp = 0;
            }
            ++i;
        }

        ((_BattleStack_*)c->edi)->creature.flags |= 0x4000000;

        if ( !o_BattleMgr->ShouldNotRenderBattle() )
        {
            __int64 res = CALL_1(__int64, __fastcall, 0x59A770, (const char*)"Regener.wav");

            memset(&o_BattleMgr->Field<char>(0x547C), 0, 40);
           
            // Пишем лог и проигрываем анимацию для всех вылеченных отрядов
            int i = 0;
            while ( healingInfo[i].stackID != -1 )
            {
                _BattleStack_* stack = &o_BattleMgr->stack[o_BattleMgr->current_side][healingInfo[i].stackID];

                sprintf(o_TextBuffer, o_GENRLTXT_TXT->GetString(415), GetCreatureName(CID_FIRST_AID_TENT, 1),
                    GetCreatureName(stack->creature_id, stack->count_current), healingInfo[i].removedDamage);

                CALL_4(void, __thiscall, 0x4729D0, o_BattleMgr->dlg, o_TextBuffer, 1, 0);

                // Отмечаем отряды, для которых необходима анимация
                o_BattleMgr->Field<char>(0x547C + 20 * o_BattleMgr->current_side + healingInfo[i].stackID) = 1;

                ++i;
            }

            // Проигрываем массовую анимацию
            CALL_4(void, __thiscall, 0x5A6AD0, o_BattleMgr, &o_BattleMgr->Field<char>(0x547C), 79, 0);
           
            // Ждём, пока воспроизведётся звук
            CALL_3(void, __thiscall, 0x59A7C0, -1, LODWORD(res), HIDWORD(res));
        }

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

    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.NewFirstAidTent");

            // Меняем параметры First Aid Tent
            _PI->WriteLoHook(0x4EE1C1, afterInit);

            // Меняем коэффициенты
            float firstAidCoefs[] = {1.0f, 3.0f, 7.0f, 11.0f};
            _PI->WriteDword(0x63EA98, (int&)firstAidCoefs[0]);
            _PI->WriteDword(0x63EA98 + 4, (int&)firstAidCoefs[1]);
            _PI->WriteDword(0x63EA98 + 8, (int&)firstAidCoefs[2]);
            _PI->WriteDword(0x63EA98 + 12, (int&)firstAidCoefs[3]);

            // Проверяем, нужен ли хил
            _PI->WriteLoHook(0x473865, findTargetsToHeal);

            // Убираем ручной контроль
            _PI->WriteCodePatch(0x4745FD, "%n", 10);

            // Делаем хил максимальным
            _PI->WriteCodePatch(0x47852C, "%n", 12);
                       
            // Хилим, пишем лог и проигрываем анимацию
            _PI->WriteLoHook(0x47852C, applyHealing);
        }
    }

    return TRUE;
}
Последний раз редактировалось AlexSpl 14 мар 2021, 19:37, всего редактировалось 1 раз.
Вернуться к началу

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

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

Сообщение Rolex » 08 ноя 2020, 22:40

1) Отображение возможного количества убитых существ при показе возможного урона. В формате (урон: X1-X2, убьет: Y1-Y2).

Эх, AlexSpl, а оказывается то, вы уже писали и выкладывали код 5-го пункта в этой теме по просьбе as239:
вот здесь и доработанную вот здесь

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

as239 немного доработал функцию getLosses, чтобы урон и кол-во убитых удваивалося для лучников с двойным выстрелом (марксменов и грандов) и баллисты. Вот здесь

После всех правок и рекомендаций RoseKavalier получилось вот это:

Я взял ваш уже исправленый вариант кода с хуком для дистанционной атаки и добавил в него доработку в функции урона от as239 для марксменов, грандов и баллисты.

Тем не менее, после тщательного тестирования я нашел еще ряд багов и проблем, которые нужно было решить.
1. Кол-во убитых просто цепляется в конец за скобками и смотрится очень инородно и некрасиво.
2. В русской локализаци Kills/Losses смотрится тоже не очень. Можно заменить на рус, но тогда в англ локализации будет русская приставка. Нужно делать двухязычным.
3. После того как as239 добавил код с удвоением для марксменов, грандов и баллисты, кол-во убитых для лучников удваивалося (для баллисты нет, об этом ниже), а вот урон оставался прежним, только по первому выстрелу (эта проблема актуальна и для последней версии FreshMod).
4. В доработке от as239 не учитывается такой момент, что у лучнков, которые стреляют дважды может остаться лишь один выстрел, а следовательно в таком случае урон и кол-во убитых удваивать ненужно. Нужна проверка на кол-во оставшихся выстрелов у текущего стреляющего отряда. Правда в оригинале в таких случаях стрелок все равно стреляет дважды и счетчик оставшихзся выстрелов уходит в минус (-1). Но в солянке кода от igrik есть фикс этого бага.
5. С абилкой Призрачных драконов, а также Вывернов Монархов наблюдаться проблемы - после снижения маскимального здоровья в 2 раза урон и кол-во убитых расчитывается неверно (за основу берется маскимальное, а не сниженное). Аналогично с абилкой Вывернов-Монархов, где каждый рауд -10% от макс и текущего, но не ниже 50%.
6. Не знаю как тестировал as239, но что касается баллисты, то именно тот его код у меня, к сожалению, не работает. Может что-то настройками компилятора или homm3.h. Для баллисты на Провинутом уровне кол-во убитых не удваивается, на Экспертном не увеличивается в 4 раза. (с лучниками все ок, кол-во убитых удваиватся, но с уроном беда и там и тут).

Проблема вот в этом участке кода:
Код: Выделить всё
      _Hero_* CurHero = o_BattleMgr->hero[o_BattleMgr->current_side];
      if (CurHero && curStackID == CID_BALLISTA) {
         int ARTI_Level = CurHero->second_skill[HSS_ARTILLERY];
         if (ARTI_Level == 2) DamageKt = 2;
         else if (ARTI_Level == 3) DamageKt = 4;
      }

UPD:
Шестой пункт решил так:
Проблема была в этой строчке:
Код: Выделить всё
int ARTI_Level = CurHero->second_skill[HSS_ARTILLERY];

Сделал:
Код: Выделить всё
int ARTI_Level = *(char*)((int)CurHero + 201 + 20);


Не знаю почему так, но видимо какие-то тонскости в работе компилятора на VS 2008 и VS 2015. Потому что на первом проблем в работе со структурой _Hero_* не наблюдается. Либо же что-то с homm3.h не так, что на VS 2015 с ней возникают проблемы.

7. А вот эта проблема тянется еще из кода AlexSpl без дополнений as239 (эта проблема так и осталась неисправленной и в последней версии FreshMod 6.2; а вот SoD_SP этот момент учитывает). Если глянуть код:

Код: Выделить всё
         _PI->WriteLoHook(0x493053, getLosses);
         _PI->WriteLoHook(0x492619, showLosses);
         _PI->WriteLoHook(0x492848, showLosses);


У нас три хука, первый высчитывает урон и кол-во убитых, два оставшихся добавляют вычесленное кол-во к оригинальным строкам из GENRLTXT.TXT.
Этот хук
Код: Выделить всё
_PI->WriteLoHook(0x492619, showLosses);

соответствует 38 строке в GENRLTXT.TXT - для обычной атаки, а этот
Код: Выделить всё
_PI->WriteLoHook(0x492848, showLosses);

298 строке - для стрелковой атаки.

Но проблема в том, что для лучников/баллисты у которых остался 1 выстрел, данные берутся не из 298 строки, а из 39 строки.
А доп хука на 39 строку у нас нет и, следовательно, когда у стреляющего отряда остается 1 выстрел, кол-во убитых не отображается. Отображается оригинальная строка без каких либо изменений.

Я еще немного доработал функцию getLosses и полностью переписал showLosses. Таким образом удалось решить первые 4 пункта (UPD: и 6-й). Изначально думал сделать как в SOD_SP, удалять закрывающуюся скобку ")" и добавлять ", kills ...)", но в таком случае проблема с отображением неверного урона для грандов и марксменов в нашей реализации (только по первому выстрелу) осталась бы.
Ведь расчитанный нами урон с учетом DamageKt мы не выводим, только MinKilled/MaxKilled. А сам урон выводится дефолтный по первому выстрелу.

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

Проверку локализации делаю просто через проверку вхождения "damage". Возможно не самый лучший вараинт, но оно работает как в англ версией игры, так и в рус с подкл Englification_Pack, ведь во всех 3 строках есть "damage" (если есть более правильный способ, интересно было бы узнать). Для марксменов, грандов и баллисты добавил проверку на кол-во оставшихся выстрелов (код удваивания урона для них будет выполнятся только если их не менее двух).
MinDamage и MaxDamage пришлось сделать глобальными поскольку они теперь используться в showLosses для формирования нашего урона с учетом DamageKt.

Если глянуть файлы перевода, то строки в рус переводе выгядят примерно так:
Код: Выделить всё
38)  Атаковать %s (%s ед. урона)
39)  Стрелять в %s (1 выстрел в запасе, %s ед. урона)
298) Выстрелить в %s (ост. выстрелов: %d, урон: %s)

я собираю все в таком виде для рус
--->
Атаковать отряд %s (урон: %s, убьет: %d-%d)
Выстрелить в отряд %s (осталось выстрелов: %d, урон: %s, убьет: %d-%d)

---
Код: Выделить всё
38)  Attack %s (%s damage)
39)  Shoot %s (1 shot left, %s damage)
298) Shoot %s (%d shots left, %s damage)

и для англ в таком:
--->
Attack %s (damage: %s, kills: %d-%d)
Shoot %s (shots left: %d, damage: %s, kills: %d-%d)


Такой формат вывода и смотрится приятней и, главное, информация в таком виде, когда изначально текст двоеточие, а потом значение, воспринимается легче.

Из оставшихся проблем:
1) Проблемы с баллистой. Что-то с проверкой, DamageKt не присваивается ни 2, ни 4.
Если точнее, то проблема вот в этих 2 строчках:
Код: Выделить всё
_Hero_* CurHero = o_BattleMgr->hero[o_BattleMgr->current_side];
int ARTI_Level = CurHero->second_skill[HSS_ARTILLERY];

В моем случае переменной ARTI_Level присваивается значение не от 0 до 3, а фиксированное: 6, которое не меняется от уровня навыка. Соотв проверка не проходит и DamageKt = 1 всегда для баллисты...
Видимо что-то с настройками компилятора, либо же кривая структура из homm3.h.

UPD: Эту проблему решил заменой
Код: Выделить всё
CurHero->second_skill[HSS_ARTILLERY]

на
Код: Выделить всё
*(char*)((int)CurHero + 201 + 20);


Хотя на VS 2008 должны работать оба варианта.

2) Нужен еще один лоухук с showLosses между двумя имеющимися
Код: Выделить всё
_PI->WriteLoHook(0xADRESS, showLosses)

по адресу, который будет подменять 39 строку из GENRLTXT.TXT для стреляющих отрядов у которых остался 1 выстрел.

3) У Призрачных драконов есть такая интерсная абилка, как Старение:
Максимальное и текущее здоровье цели снижается на 50%. Если верхнее существо в целевом отряде имеет не полное здоровье, но больше половины, то полученный ранее урон вычтется из сниженного здоровья; если половину здоровья или меньше, то текущее здоровье снизтся до 1.
20% шанс срабатывания при ударе. Действует только на живых. Длительность - 3 раунда.

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

Похожая проблема наблюдается и с Вывернами-Монархами:
Каждый раунд, включая момент наложения, максимальное и текущее здоровье цели будет снижаться на 10%, но не более, чем на 50%. Округление вверх. Если верхнее существо имеет более 10% здоровья, то полученный ранее урон вычтется из сниженного здоровья; если 10% или меньше, то текущее здоровье снизтся до 1.
30% шанс срабатывания при ударе. Действует только на живых. Сниженное здоровье не восстанавливается до конца боя. Яд снимается Лечением, но не Снятием заклинаний или Антимагией. После воскрешения отряда, на который был наложен Яд, эффект пропадает, но уже сниженное здоровье так и остаётся сниженным. Если на цель наложена Старость, то 10% берётся от уже сниженного здоровья.

Хотя с артами которые добавляют здоровье вроде как работает. Кольцо здоровья (+1) + Кольцо жизни (+1) + Сосуд жизненной силы (+2) = Эликсир жизни (+25% к базовому здоровью каждого существа + 4 ед. за составные).
Но нужно еще тестировать.

Есть еще момент со Сказочными драконами, они ведь первые 5 раз не стреляют и не атакуют, а колдуют. Соответственно ничего не отображается по урону и кол-ву убитых. Но это уже скорей должен делать не этот плагин, а тот, который выводит урон и кол-во убитых существ по залинаниям.
После 5 раз колдовства, атака уже будет обычной и это уже будет обрабатывать этот плагин.

К сожалению, для решения оставшихся проблем моих знаний на данном этапе явно недостаточно...
---
К слову в моей реализации showLosses была одна проблема, которую все же удалось решить. Проверку на то, строку какого вида собирать для стрелков или для обычного отряда с рукопашной атакой, я изначально проверял по количеству оставшихся выстрелов.
Так вот выстрелы у юнита могут быть, но на одной из соседствующих с ним клетке может стоять вражеский отряд и вот здесь у меня выводилась строка как для стрелка (так как выстрелы у него еще были), хотя должна как для обычного отряда.
Была идея брать коорддинаты нашего стрелкового отряда и вражеского и вычислять расстояние между ними по формуле d = sqrt((x2-x1)^2 + (y2-y1)^2) и если оно равно sqrt(2) или 1 (d <= sqrt(2)), то собирать строку как для обычного отряда.

Но все же были ощущения, что можно как-то получить тип атаки и это должно быть гораздо проще. При наведении курсора на юнита он меняется на меч, сломленную стрелу или прямую. Так вот перед тем как выбрать какой тип курсора выводить где-то до этого определяется тип атаки. Ее нужно было получить.
Потом обратил внимание на фикс для проверки рукопашной атаки, который давал RoseKavalier для as239, чтобы кол-во убитых не удваивалось, когда лучники не стреляют, а бьют в ближнем бою:
Код: Выделить всё
IsMelee = (*(int*)o_BattleMgr->Offset(0x132DC) == 7);

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

Добавил также фикс igrik'a из солянки его кода, который запрещает второй выстрел для баллисты и стрелков с двумя выстрелами, если боезапас закончился. Это для тех случаев, когда в запасе остается только 1 выстрел и после первого уже нечем делать второй. Правда у баллисты декремент не рабоатет, это нужно фиксить.
Если без этого фикса, то гранды и марксмены после первого делают и второй, а счетчик оставшихся выстрелов уходит в минус. Выглядит так: 24(-1). Это однозначно баг, такого быть не должно.
И очень странно, но этот баг SOD_SP не исправляет (а вот HotA исправляет)...
---
!!! Ну и самый хардкор, это если у кого-то будет время и желание этим занятся.

RoseKavalier писал(а):

It's a good idea which I never implemented because I could never figure out what to do with Crusaders and Wolf Raiders in counterpart.

as239 писал(а):

А вот у волков и крестоносцев, очень сложно подсчитать их двойной урон т.к. перед вторым ударом им идет ответка.


Пока что это так никто и не сделал. В SOD_SP, WoG/ERA и в HotA этого нет.

То что можно точно сделать, так это просто удваивать урон и кол-во убитых в тех случаях, когда враг уже отвечал в текущем раунде и уже не ответит (сложность будет, чтобы проверить будет ли ответка. Для грифона нужна отдельная проверка на 2 ответки. А Королевксих в исключение).
Если же вражеский отряд погибнет от первого удара, то здесь можно выводить как урон за один первый удар, так и двойной (хотя правильней, наверное, за двойной).

Кстати, если крестоносцы или налетчики первыми во время своего хода атакуют врага у которого абилка - враг не отвечает на атаку, то после ответки врага они все равно наносят второй удар.

А вот если Налетчики или Крестоносцы ударят первыми Скопикору и в ответке прилетит еще их абилка Парализующее жало, то в таком случае второй удар они не производят. То есть принцип работы Паралича (абилка Скопикоров) аналогичен Слепоте.
Точно также если ответка Боевых единорогов будет сопровождаться Ослеплением, второго удара не последует. Как и в случае со Слепотой при Параличе отряд будет пропускать свой ход.
Но все же есть одно отличие. Если ударить существо под Слепотой, то ответка получится вполсилы, то есть урон снижается на 50%, а вот если ударить существо под Параличем, то ответка получить только на четверть от полноценной (25%), то есть урон снижается на 75%.

Принцип работы Окаменения (абилка Королевы Медуз и Великие Василиски) тоже схожа с вышеописанными в плане пропуска хода, но здесь уже уполовинивается урон от атаки не пострадавшего отряда, а именно атакующего. Как у Единорогов, так и у Скопикор абилка работает на ответке.

Был забавный случай: Архангел атакует Боевых Единорогов и получает ответку вместе с ослеплением, и в это же время Архангелу сразу выпадает Моралька на доп ход, но поскольку только что на него была наложена Слепота он сразу же пропускаетсвой свой дополнительно полученный ход.

С абилками конечно все сложно, но изначально можно сделать и без их учета.

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

- Ржавые драконы: Кислотное дыхание: -3 защиты и доп. урон (+25 единиц от 1 дракона) с шансом 20%. Вот здесь интересно урон фиксированный, но с каждой ответкой и атакой он нарастает до 25 ед на дракона.
- Птицы Грома: Удар Молнии: +10 единиц доп. урона от 1 птицы с шансом 20%.
- Могучие горгоны: Смертельный взгляд: 10% шанс убить от 1 до N/10 + 1 существ в целевом отряде, где N – число Могучих горгон.
- Султаны ифритов: Огненный щит 20% от нанесенного противником урона (% урона берется после расчета всех бонусов к атаке, но до расчета всех штрафов и модификаторов защиты цели).
 Подробней
Изображение

- Призрачные драконы: 20% шанс, что юниты целевого отряда противника состарятся (-50% от базового здоровья всех существ в отряде). Это актуально если крестоносцы или волки атакую уже с наложенной старостью, соотв после ответки они понесут двойной урон и второй удар их нанесет в два раза меньше урона противнику, чем если бы этот второй удар был без старости.
- Выверны-Монархи: 30% шанс Яда (при ударе) - каждый раунд, включая момент наложения, максимально и здоровье цели будет снижаться на 10%, но оно не может снизиться более, чем на 50%. Опять же актуально если атака идет уже с наложенным ядом.
- Черные рыцари: Проклятие - мин урон. Если после ответки оно будет наложено, то второй удар будет с мин уроном.
- Рыцари Смерти: Смертельный удар (двойной урон) + Проклятие (мин урон) с шансом 20%.
- Мумия: Проклятие (мин урон) с шансом 50%.
- Зомби: Болезнь на 3 раунда с шансом 20%: -2 атаки и защиты. Если после ответки была наложена Болезнь, то как-бы второй удар будет немного меньшей силы в силу того, что атака будет на 2 меньше. Хотя это капля в море, учитывая то, что урон итак будет оч приближенный ([min ~ 2x max]).
- Змии: Снятие благоприятных спеллов, может сыграть свою роль в том случае, если крестоносец под Благословением, например, и любым другим заклом, который увеличивает его атаку/урон. Второй удар в таком случае будет обычным.
- Стрекозы: Тут еще в придачу снтию благоприятных идет и Слабость на Продвинутом уровне (-6 к атаке), что тоже скажется на силе второго удара, если в ответке она будет наложена.

+ Тут еще момент если после первого ответка от Боевых Единорогов, Скорпикор, Королевы Медуз и Великих Василисков будет с их абилкой, то второго удара опять же не последует. Я даже и не знаю, а возможно ли это вообще зарание узнать выпадет ли абилка (или удача) в ответке.
А если Крестносцу или налетчику выпадает удача под первый или второй удар, этот удар нужно удвоить в плане урона и кол-ва убитых. Но это, по-моему, учитывать и ненужно, мы же урон отображаем как если бы атака была обычной без удачи, а в ответке не прилетит что-то вроде слепоты/паарлича/окаменения и не отберет второй удар.

AlexSpl писал(а):

Например, попробуйте просчитать полный урон стека Крестоносцев, атакующих Ифритов или Могучих Горгон (ого, я реально не помню, работает ли их абилка на ответке).

Проверил, работает, только если у Могучих горгон это 10% шанс срабатывания абилки, то у Султанов Ифритов он 100%, то есть срабатывает всегда после каждой атаки врага. После каждого удара ксрестоносца он получит урон Огненного щита, то есть 2 раза (независимо от того была ответка или нет). Но нам важен только урон после первого удара.


Код на данный момент:

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

using namespace std;

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

int MinDamage, MaxDamage, MinKilled, MaxKilled, FullHealth, shotsLeft, countCurrent;
string nameSingle, namePlural;
bool IsMelee;

int __stdcall getLosses(LoHook* h, HookContext* c)
{
   int DamageKt = 1;

   _BattleStack_* foeStack = *(_BattleStack_**)(c->ebp + 8);
   _BattleStack_* curStack = o_BattleMgr->GetCurrentStack();
   
   int foeStackID = foeStack->creature_id;
   int curStackID = curStack->creature_id;

   nameSingle = o_pCreatureInfo[foeStackID].name_single;
   namePlural = o_pCreatureInfo[foeStackID].name_plural;
   
   shotsLeft = curStack->creature.shots;
   countCurrent = foeStack->count_current;


   IsMelee = (*(int*)o_BattleMgr->Offset(0x132DC) == 7);

   // Проверка оставшегося кол-ва выстрелов. Удваивание урона для марксменов, грандов и баллисты только при наличии у них в запасе не менее 2-х выстрелов.
   if (shotsLeft > 1) {
      if ((curStackID == CID_MARKSMAN || curStackID == CID_GRAND_ELF) && (!IsMelee))
         DamageKt = 2;

      _Hero_* CurHero = o_BattleMgr->hero[o_BattleMgr->current_side];
      if (CurHero && curStackID == CID_BALLISTA) {
         int ARTI_Level = *(char*)((int)CurHero + 201 + 20);
         // int ARTI_Level = CurHero->second_skill[HSS_ARTILLERY];
         if (ARTI_Level == 2) DamageKt = 2;
         else if (ARTI_Level == 3) DamageKt = 4;
      }
   }
      
   MinDamage = DamageKt * c->edi;
   MaxDamage = DamageKt * c->eax;
   
   MinKilled = MinDamage / foeStack->full_hp;
   if (MinDamage % foeStack->full_hp >= foeStack->full_hp - foeStack->lost_hp) ++MinKilled;
   if (MinKilled > foeStack->count_current) MinKilled = foeStack->count_current;

   MaxKilled = MaxDamage / foeStack->full_hp;
   if (MaxDamage % foeStack->full_hp >= foeStack->full_hp - foeStack->lost_hp) ++MaxKilled;
   if (MaxKilled > foeStack->count_current) MaxKilled = foeStack->count_current;

   return EXEC_DEFAULT;
}

int __stdcall showLosses(LoHook* h, HookContext* c)
{
   string Buffer = o_TextBuffer;
   string NewBuffer, Name;

   // получаем имя вряжеского стека (если текущее кол-во юнитов в стеке больше 1, то в множественном числе, иначе в единственном числе).
   if (countCurrent > 1) Name = namePlural; else Name = nameSingle;

   // проверяем родной буфер на вхождение "damage", если оно есть, то формируем строку на англ языке, иначе на рус.
   if (Buffer.find("damage") != -1) {
      // Если у текущего отряда героя есть выстрелы, а также его атака не рукопашная, то есть в соседствующих гексах нет вражеского отряда, то формируем строку для стреляющего отряда, иначе для обычного (англ версия).
      if (shotsLeft > 0 && !IsMelee) NewBuffer = "Shoot " + Name + " (shots left: " + to_string(shotsLeft) + ", damage: "; else
                     NewBuffer = "Attack " + Name + " (damage: ";
   }
   else {
      // Если у текущего отряда героя есть выстрелы, а также его атака не рукопашная, то есть в соседствующих гексах нет вражеского отряда, то формируем строку для стреляющего отряда, иначе для обычного (рус версия).
      if (shotsLeft > 0 && !IsMelee) NewBuffer = "Выстрелить в отряд " + Name + " (осталось выстрелов: " + to_string(shotsLeft) + ", урон: "; else
                     NewBuffer = "Атаковать отряд " + Name + " (урон: ";
   }
   
   // если мин урон равен макс (под Благословением, например), то добавляем к нашей строке только один, иначе оба.
   if (MinDamage == MaxDamage) NewBuffer.append(to_string(MaxDamage)); else
                        NewBuffer.append(to_string(MinDamage) + '-' + to_string(MaxDamage));

   // переганяем стринг в массив чаров для корректного вывода.
   char* NewBufferWrite = new char[NewBuffer.size() + 1];
   strcpy(NewBufferWrite, NewBuffer.c_str());

   if (Buffer.find("damage") != -1) {
      if (MinKilled == MaxKilled) sprintf(o_TextBuffer, "%s, kills: %d)", NewBufferWrite, MaxKilled); else
                           sprintf(o_TextBuffer, "%s, kills: %d-%d)", NewBufferWrite, MinKilled, MaxKilled);
   }
   else {
      if (MinKilled == MaxKilled) sprintf(o_TextBuffer, "%s, убьет: %d)", NewBufferWrite, MaxKilled); else
                           sprintf(o_TextBuffer, "%s, убьет: %d-%d)", NewBufferWrite, MinKilled, MaxKilled);
   }

   return EXEC_DEFAULT;
}



// запрет на второй выстрел для баллисты и стрелков, если боезапас закончился (после первого выстрела в запасе 0)
int __stdcall Y_monstreShoot(LoHook* h, HookContext* c)
{
   _BattleStack_* mon = (_BattleStack_*)c->esi;

   if (mon->creature.shots < 1) {
      c->return_address = 0x43FFFC;
      return NO_EXEC_DEFAULT;
   }

   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.ShowLosses");

         // второй выстрел марксменами и грандами
         _PI->WriteLoHook(0x43FF92, Y_monstreShoot);

         // второй выстрел баллистой
         _PI->WriteLoHook(0x43FFF4, Y_monstreShoot);

         _PI->WriteLoHook(0x493053, getLosses);
         _PI->WriteLoHook(0x492619, showLosses);
         _PI->WriteLoHook(0x492848, showLosses);
      }
   }

   return TRUE;
}

--------------------------------------------------------------------------------------------------

2) Отображение возможного урона и количества уничтоженых существ у одиночной мишени при касте атакующих заклинаний и лечения, и эффекта заклинаний воскрешения сколько будет восстановлено/воскрешено (как для героя, так и для колдующих существ).

В формате
(урон: X, убьет: Y)
для ударных спелов
и
(восстановит здоровья: X, существ: Y)
для воскрешающих (воскрешение, Поднятие мертвецов, Жертва)

+ сюда же Каст заклов (Питлорды + Архи + Сказочные драконы)

Воскресить отряд Name (восстановит здоровья: X, существ: Y)
Поднять демонов из тел павшего отряда Name (восстановит здоровья: X, Демонов: Y)
Направить заклинание NameSpell на отряд Name (осталось раз колдовать: N, урон: X, убьет: Y)

Сказочный может 5 раз колдовать, потом - обычная атака.

 Пример
Изображение
Изображение
Изображение
Изображение


То что до скобок выводится из GENRLTXT.TXT. Тут все проще чем с уроном при атаке. Никаких ответок и двойных уронов и строку пересобирать ненужно, только добавить то, что в скобках.

Вот здесь наверно для кажого закла нужно делать отдельно. Не знаю реально это все зделать в одном хайхуке или же нужно писатиь на каждый спелл отдельный хук.
Решить бы с Питлордами, Архами и Сказочными и пример на пару заклов ударных и для воскрешения, а дальше я думаю смог бы и сам по аналогии. Это конечно если под каждый спелл не понадобится свой хук с уникальным адресом. Иначе нужны будут еще и адреса.

--------------------------------------------------------------------------------------------------

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

Нужно, чтобы для Видений показывался их радиус действия, для Стены Огня и Минного Поля - их урон, для Лечения - количество исцеляемого здоровья, для Воскрешения и Поднятия Мертвецов - максимальное количество восстанавливаемого здоровья, для Жертвы - сколько дополнительно здоровья за каждое жертвуемое существо будет восстановлено, для Гипноза - максимальное здоровье подчиняемого стека, для Элементаля Огня, Элементаля Земли, Элементаля Воды и Элементаля Воздуха показывается количество призываемых элементалей.

 Пример
Изображение Изображение Изображение Изображение


Все формулы можно найти в ФизМиге. Сложность в том, чтобы правильно получить текущее значени силы магии героя и корректно как с карты приключения, так и в бою дописывать эту доп инфу по спеллам в книгу каждое в свое окно.
Если это возможно сделать в одном хайхуке, то мне будет достаочно примера с парочкой разноплановых спепллов. С другими, думаю, по аналогии и сам бы доделал. Если же для каждого потребуется писать отдельный хук по своему адресу, то нужны будут еще и адреса.

--------------------------------------------------------------------------------------------------

4) Имеется код от igrik'a, который исправляет ряд багов со стрельбой. В оригинале у Катапульты и Баллисты не работает декремент запаса выстрелов. А также у циклопов стреляющих по стенам также не срабатывал декремент по запасу выстрелов (он работал только при стрельбе по существам).
Плюс сюда входит правка бага для марксменов и грандов, когда они делают два выстрела, когда в запасе остался только один. Код исправляет почти все это, кроме декремента Баллисты. Хотелось бы его поправить, чтобы он работал также, как и у Катапульты. Эти все баги со стрельбой, кстати, были исправленны в HotA.

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

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

// исправление багов со стрельбой
int __stdcall Y_setActStack(LoHook* h, HookContext* c)
{
   _BattleStack_* mon = (_BattleStack_*)(int)(c->esi - 656);

   // если выстрелов у катапульты или балисты больше нет
   // то делаем пропуски передачи хода этим машинам
   if (mon->creature_id == 145 || mon->creature_id == 146) {
      if (mon->creature.shots < 1) {
         c->return_address = 0x464DAD;
         return NO_EXEC_DEFAULT;
      }
   }
   // если выстрелов у циклопов больше нет
   // то забираем флаг "катапульта"
   if (mon->creature_id == 94 || mon->creature_id == 95) {
      if (mon->creature.shots < 1) {
         if (mon->creature.flags & 32) {
            mon->creature.flags -= 32;
         }
      }
   }
   return EXEC_DEFAULT;
}

// создание инкремента боезапаса при атаке осадных стен "катапульта"
int __stdcall Y_catapultaShoot(LoHook* h, HookContext* c)
{
   _BattleStack_* mon = (_BattleStack_*)c->edi;
   mon->creature.shots -= 1;

   return EXEC_DEFAULT;
}

// запрет на второй выстрел, если боезапас закончился
// для баллисты и стрелков
int __stdcall Y_monstreShoot(LoHook* h, HookContext* c)
{
   _BattleStack_* mon = (_BattleStack_*)c->esi;

   if (mon->creature.shots < 1) {
      c->return_address = 0x43FFFC;
      return NO_EXEC_DEFAULT;
   }

   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.FixShotsBugs");
         
         // исправления багов со стрельбой
         // при передаче хода стеку
         _PI->WriteLoHook(0x464D75, Y_setActStack);

         // при стрельбе по стенам (катапульта, циклопы)
         _PI->WriteLoHook(0x445CF9, Y_catapultaShoot);

         // второй выстрел монстрами
         _PI->WriteLoHook(0x43FF92, Y_monstreShoot);

         // второй выстрел баллистой
         _PI->WriteLoHook(0x43FFF4, Y_monstreShoot);
      }
   }

   return TRUE;
}


--------------------------------------------------------------------------------------------------

5) AlexSpl, вот здесь Вы писали код по запрещенеию водных заклинаний и навыков на картах без воды. Его, кстати, тоже можно в готовых плагинах выложить.

Тут если пересмотреть всю тему немало плагинов насобирается (многие от AlexSpl), которых нет в готовых плагинах.

А с артефактами сложно будет?
Нужно также запретить на картах без воды такие артефакты: Ожерелье морского провидения, Шляпа капитана и сборный Шляпа адмирала.

Плюс очень нужен плагин, который независимо от выбранной карты полностью запретит в игре такие артефакты, как Крылья Ангела, Сфера Запрещения и Плащ Отречения.

--------------------------------------------------------------------------------------------------

6) Нужно уменьшить единицы передвижения, которые дают Перчатки всадника с 300 до 200, а те, которые дают Сапоги-скороходы с 600 до 400.
В гайде, которые вы давали (btb2) такие адреса:

23E870 Equestrian's Gloves
23E8E0 Boots of Speed

На я сопоставлял те адреса, которые были по артам на Некромантию в гайде с теми, которые вы использовали в плагине по этим же артам, когда помогали мне с плагином на эту Некромантию. Они совершенно разные.
То есть если со вторичными навыками я могу просто взять адрес с гайда и прибавить к нему 0x40000, то с артам так уже не получается, нужно прибавлять что-то другое.

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

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

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

struct ArtInfo {
   char* Name;
   char* Desc;
};

ArtInfo Equestrians_Gloves = {
   "Перчатки всадника",
   "{Перчатки всадника}\n\nУвеличивают продолжительность перемещения героя по суше на +200 единиц в день."
};

ArtInfo Boots_of_Speed = {
   "Сапоги-скороходы",
   "{Сапоги-скороходы}\n\nУвеличивают продолжительность перемещения героя по суше на +400 единиц в день."
};

int __stdcall changeArtDesc(LoHook* h, HookContext* c)
{
   *(int*)(*(int*)0x660B68 + 70 * 32) = (int)Equestrians_Gloves.Name;
   *(int*)(*(int*)0x660B68 + 98 * 32) = (int)Boots_of_Speed.Name;

   *(int*)(*(int*)0x660B68 + 70 * 32 + 0x10) = (int)Equestrians_Gloves.Desc;
   *(int*)(*(int*)0x660B68 + 98 * 32 + 0x10) = (int)Boots_of_Speed.Desc;
   
   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.SpecDesc");

         
      //   _PI->WriteDword(0x63E870, 200); // btb2 (Equestrian's Gloves): 23E870 // нужно понизить с 300 до 200
      //   _PI->WriteDword(0x63E8E0, 400); // btb2 (Boots of Speed): 23E8E0 // нужно понизить с 600 до 400
      //   _PI->WriteDword(0x4E4445 + 2, 200);
      //   _PI->WriteDword(0x4E3FE5 + 2, 400);

         _PI->WriteLoHook(0x44CCB5, changeArtDesc);
      }
   }

   return TRUE;
}


--------------------------------------------------------------------------------------------------

7) Имеется ваш плагин, который вы писал для as239, который при наведении курсора мыши на Ученого в информационной строке в скобках отображает, что он даст (Заклинание, Первичный или Вторичный навык). Так вот мне нужно его немного переделать.
Нужно, чтобы при наведении, он точно показывал, что даст тот или иной ученой. Плюс в этот же плагин нужно добавить Хижину ведьмы, чтобы при наведении курсора на нее в инфо строке выводился точный вторичный навык, который в ней можно получить.

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

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

char TxtBuffer[50];

int __stdcall showScholarInfo(LoHook* h, HookContext* c)
{
   // Учёный?
   if (c->eax == 0x51) {
      int ScholarType = (*(int*)c->ebx << 0x1D) >> 0x1D;
      switch (ScholarType) {
      case 0:
         sprintf(TxtBuffer, "%s %s", (char*)c->edi, "(stat)");
         break;
      case 1:
         sprintf(TxtBuffer, "%s %s", (char*)c->edi, "(skill)");
         break;
      case 2:
         sprintf(TxtBuffer, "%s %s", (char*)c->edi, "(cast)");
         break;
      }

      c->edi = (int)TxtBuffer;
   }

   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.Scholar");

         _PI->WriteLoHook(0x40D059, showScholarInfo);
      }
   }

   return TRUE;
}


--------------------------------------------------------------------------------------------------

8) Имеется ваш плагин, который вы опять же писали для as239, это код для отображения кол-ва охранников в банке. Вы позже добавили туда еще и Утопию. Но без Склепа (s239 он был ненужен). Мне же наооброт нужен Склеп.
Плюс нужно, чтобы после захвата внешнего жилища, по ПКМ по нему отображалось кол-во доступных существ для найма (тут уже точное кол-во), вот так:

 Пример
Изображение Изображение

"Принадлежит ...

Доступные существа:
Имя - кол-во
"

Аналогично сделать и для Лагеря беженцев.

В жилищах первого уровня существа предлагаються бесплатно, а потому тут еще к тому сообщению при посещении: "Name - хорошие воины. Хотите ли взять их к себе на службу...", надо бы добавить (как в банках сразу, только точное):
Доступные существа:
Имя - кол-во


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

using namespace std;

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

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

int __stdcall showGuards(LoHook* h, HookContext* c)
{
    bool isUtopia = h->GetAddress() == 0x4A1E56;

    // Получаем состояние банка
    _CrBankState_* bankState = (_CrBankState_*)CALL_1(int, __fastcall, 0x405D80, isUtopia ? c->edi : c->ebx);

    int CreaturesCount = bankState->defenders.GetCreaturesCount();
   
    string str = (char*)c->ecx; // Оригинальное сообщение
    str = str + "\n\nAmount: " + getInterval(CreaturesCount);
       
    // Утопия?
    if ( isUtopia ) {
        str = str + " (" + getInterval(bankState->defenders.count[0]) + ", " + getInterval(bankState->defenders.count[1]) + ", " +
            getInterval(bankState->defenders.count[2]) + ", " + getInterval(bankState->defenders.count[3]) + ")";
    } else
    // Если есть грейд
    if ( bankState->defenders.type[2] != bankState->defenders.type[0]) {
        str = str + " (" + getInterval(CreaturesCount - bankState->defenders.count[2]) + ", " + getInterval(bankState->defenders.count[2]) + ")";
    }
           
    // Передаём адрес текстового буфера в качестве аргумента для диалога
    sprintf(o_TextBuffer, "%s", 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("HD.Plugin.ShowGuards");

            _PI->WriteLoHook(0x4A13E6, showGuards);
            _PI->WriteLoHook(0x4A1E56, showGuards);
        }
    }

    return TRUE;
}


--------------------------------------------------------------------------------------------------

9) И еще один плагин по перестройке Гильдии магов. Ранее вы также подобный уже писали, когда HW Rulez еще не был встроен в HD-мод, а подключался отдельным плагином. Вроде патчили его. Сейчас он интегрирован непосредственно в мод.

Вот ЗДЕСЬ!!!.

Так вот, во-первых, чтобы Гильдия перестраивалась, нужно каждый раз после выбора и во время настройки случайной карты, включать в Опциях Перестройку (галка с Перестройкой Библиотеки будет установлена автоматически). Во-вторых, перейстроить эту Гильдию можно только после ее полной отстройки (все уровней), то весь смысл этой фичи теряется.

Хотелось бы иметь плагин, который будучи подключенным к моду, устанавливал бы эту галку по умолчанию (как с очередью существ) и постоянно держал включенной (отдельного твика для нее я что-то не нашел), пока подключен сам плагин. Ну и самое главное нужно сделать, чтобы перестроить Гильдию можно было после каждого уровня, а не после максимального.

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

--------------------------------------------------------------------------------------------------

10) Было бы неплохо еще иметь плагин, который бы выводил информация о стоимости улучшения существ по клику правой кнопкой мыши по кнопке апгрейда в окне существа (даже если кнопка неактивна) и при наведении на кнопку апгрейда в инфо строке).

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

 Пример
Изображение


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

--------------------------------------------------------------------------------------------------

11) Увольнение героя в городе и на экране обзоре королевства.

as239 писал(а):

Сделал чтобы кнопка удалить была доступна для героя в городе, при открытии с карты приключений.
_PI->WriteByte(0x4E1B98, 0x74);
Но не уверен в правильности решения.


igrik писал(а):

Это не верное решение.
Верное -> смотри по этому адресу 0x4E1CE7



Есть ли верное стабильное решение этой задачи?

--------------------------------------------------------------------------------------------------

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

 Примерно так:
Изображение Изображение


То есть при нажатии на эту кнопку отмены, опыт добавляется, первичный навык повышается, а вот вторичный навык не изучается. Если все же случайно был отмечен какой-то вторичный навык (выделен рамкой по клику мыши) и кнопка подтверждения стала активной, то кнопка отмены при этом остается активной всегда и по ее нажатию выбранный навык все равно не изучается.

Например, начиная где-то с 24 урвоня, когда у героя уже заняты все слоты под навыки, и все навыки развиты на Эксперте и дальше повышается только первичный навык.
Когда до этого дойдет с кнопкой отмены, то уровень героя вероятно будет повыше, в таком случае эту кнопку можно просто не выводить/скрывать/делать неактивной. Хотя... самый простой и в то же время лучший вараинт, это, наверное, сделать так, чтобы кнопка отмены была активной только в тех случаях, когда герою для изучения предлагаються два абсолютно новых навыка (базового уровня), которых у него еще нет, во всех остальных случаях делать ее просто неактивной.

--------------------------------------------------------------------------------------------------

13) Есть три ключевых спелла - Полет, Дверь измерений и Городской портал, которые зачастую приводят к победам как в офлайне, так и в онлайне. На турнирах они вообще либо запрещаються, либо жестко ограничиваються в количестве использования.
Так вот было бы круто иметь плагин, который сделает верятность выпадения этих спеллов в Гильдиях со 100% вероятностью у всех. То есть при отстройки 4 уровня всегда должен быть Городской портал и + одно, другое заклинание 4-го уровня, которое будет выбираться как обычно случайным образом с шансом по умолчанию. Для Башни с Библиотекой 1 Гордской портал и + 2 других случайных.
А вот на 5 уровне случайным образом всегда должен выбиратся один из двух спеллов, либо Полет, либо Дверь измерений. Для Башни с Библиотекой в один слот обязательно должен попадать либо Полет, либо Дверь измерений, а в другой, любое другое исключая эти оба (то есть чтобы не было такого, что Полет и Дверь будут вместе).

Изображение Изображение Изображение


Единственный недостаток такого решения это то, что отличное от Полета или Дверей Измерений заклинание можно будет получить только в Башне с отстроенной Библиотекой. Но я все же не думаю, что это большая проблема, так как 5-уровневых спеллов не так уж и много. К примеру, Волшебное зеркало вообще никто не использует, а Жертву, Взрыв и Элементалей можно получить и с других источников, таких как Свитки, Ученые, Пирамида.

Зато плюсы на лицо. Исключаем рандом и игроки практически в равных условиях. Есть конечно нюансы, но все же на мой взгяд такое решение куда лучше, чем запрещать эти спеллы, либо же жестко их ограничивать в использовании. Можно конечно увеличить цену заклинаний по мане и расходу очков перемещения, но как по мне, то первый вариант будет куда лучше.

***
UPD: Еще было бы неплохо сделать, чтобы при постройке Гильдии магов 1 уровня Медлительность и Ускорение, а при постройке Гильдии магов 2 уровня и Слепота, выпадали у всех со 100% вероятностью.

--------------------------------------------------------------------------------------------------
14) Болезнь, Окаменение, Паралич, Яд, Старость, Страх. Все эти заклы (магические способности) из абилок существ, которые можно смело делать отдельными спеллами и добавлять в книгу магии. Только продумать эффект на разных уровнях Магии.

Тот же Огненный щит Султана Ифрита есть отдельным спеллом. У него урон от щита 20%, что соотв Базовому уровню, на Продвинутом - 25%, Эксперт - 30%. А вот спеллы перечисленные выше можно встретить только в абилках.

Но одно из заклинаний - мифическое заклинание Страх, разработчики все же планировали добавить еще в RoE. Но то что планировалось, так и не было реализовано.

Изображение

СТРАХ: Боевое заклинание. Относится к магии разума, т.е. не действует на нежить, элементалей, големов и существ с защитой от магии разума.
Школа магии: Магия земли.
Уровень магии: 4.
Стоимость: 16 маны (12 при развитии Школы магии).
Длительность: СМ раундов, где СМ - Сила магии героя.
Эффект без Школы или Базовая: Цель теряет способность атаковать и её скорость снижается на 25%.
Продвинутая Школа магии: То же, но скорость цели -50%.
Экспертная Школа магии: То же, но скорость цели -75%.

---
Интересо, что абилка у Лазурных тоже Страх. Но там принцип работы немного другой, так как это немагическая способность. Там 10% шанс того, что вржеский отряд пропустит свой очередной ход.
В заклинании Страх, как я понимаю, планировалось что существо ход пропускать не будет, а просто будет ограничиваться (замедляться) в передвижении и если даже и доходит до вражеского отряда, то ударить не может, только стать рядом.
И тут два варианта: можно оставить так, а можно сделать как у Лазурных, только процент на пропуск хода сделать 25%/50%/75%.
---
Данное заклинание можно было встретить лишь в ранних версиях Возрождения Эрафии (до v.1.3) и лишь в Пирамидах (в Пирамидах можно было получать некоторые заклинания 4 уровня, например Бешенство).
По сути это заклинание разработчики вырезали из финальной версии, но про Пирамиды забыли. Описание эффекта основано на информации из ресурсов игры. При попытке использовать это заклинание не происходило никакого эффекта (баг).
В версиях Клинок Армагеддона (АВ), Дыхание смерти (SоD) и Complete заклинание Страх было заменено на заклинание Гром титана (Titan's Lightning Bolt). Однако pcx файлы заклинания Страх остались.
---------
Но среди всех спеллов из абилок, мне больше всего заходит абилка Дракона-привидения (Призрачного дракона) - Старение. Остальные либо малоэффективны, либо очень схожи по принципу своей работы с уже имеющимися и поэтому не интересны.

Изображение

Название: СТАРЕНИЕ
Школа магии: Магия земли.
Уровень магии: 5.
Стоимость: 25 маны (20 при развитии Школы магии).
Длительность: СМ раундов, где СМ - Сила магии героя.
Эффект без Школы или на Базовая: Снижает максмальное здоровье выбранного вражеского отряда на 25%.
Продвинутая Школа магии: Снижает максмальное здоровье выбранного вражеского отряда на 50%.
Экспертная Школа магии: Снижает максмальное здоровье всех вражеских отрядов на 50%.

Либо же на Эксперте можно зделать только для одного выбранного отряда, но тогда увеличить процент. Есть три варианта: 40%/50%/60%, 40%/60%/80%, 25%/50%/75%.

Дело в том, что в игре нет заклинаний которые бы снижали макс здоровье существ. Среди абилок - это Старение (Призрачный дракон) и Яд (Выверны-Монархи). А среди артов два кольца и сосуд, там капля, но в сумме правда если собрать Эликсир жизни, то уже +25% к максимальноу + 4. Но это повышение.
А вот артефактов или заклинаний, которые бы снижали макс здоровье вражеских отрядов в игре реально нет.

Команда HotA до заклинаний толком и недобралась пока что. Немного Армагеддон ослабили и Защиту от Магии Воды/Огня/Земли/Воздуха усилили и на этом вроде бы все. Остальные спеллы не балансировали. Страх не восстанавливали и новых спеллов не добавляли пока что.

Кстати, реализация Маскировки из темы с готовыми плагинами мне реально понравилась. Ben80 и AlexSpl потрудились на славу.

Не знаю насколкьо сложно будет плагином добавить абсолютно новое заклинание. Это нужно сделать чтобы оно и в книгу добавлялось, и отображалось в ней, и генерилось вместе с осталиными спеллами в Гильдиях, Свитках, Ученых, Пирамиде и тд. Ну хоть картинки и анимацию рисовать ненужно. Но, безусловно, идея добавления нового спелла на основе магической абилки через плагин интересная.
----------
15) В оригинале есть небольшой баг, когда Забывчивость колдуется на все вражеские отряды уже на Продвинутом уровне магии воды, хотя это должно быть только на Эксперте. Есть ли где-то фикс этого бага или не подскажите как это исправить?
----------
16)
Специализация Некромантия для Исры и Видомины уменьшить до 2% за уровень. С изменением описания.
Специализация для героев Оружие (Атака) (Цитадель - Крэг Хак, Гундула) и Доспехи (Защита) (Оплот - Мефала, Башня - Нила, Крепость - Тазар) уменьшить до 2% за уровень. С изменением описания.
----------
17) Плагин, который будет отображать расходуемые очки движения при зажатой клавише "Alt".
----------
18) Плагин, который даст возможность активировать героя через Shift + Левый Клик Мышки.
----------
19) Плагин, который разрешит всех героев из кампаний. Герои появляются со стартовой армией своего города.
----------
20) Плагин, который будет выводить остаток здоровья отряда (вместо максимального), во всплывающем по бокам, окне (с вкл опцией все данные в Сведениях о существах в Настройках боя).
----------
21) Плагин, который корректно снимет лимит на на количество капитолиев (в каждом городе должна быть возможность построить Капитолий).
----------

22) Из кода по обновлению описания Хижины:
Код: Выделить всё
#define o_ADVEVENT (*(_TXTEX_**)0x696A68)


А не подскажите адрес только теперь на файл ARRAYTXT.TXT ?
-------------------------------------------------------------------------------------------

23) А как можно наиболее правильно определить основной язык игры из плагина. Дело в том, что может быть установленна русская версия игры, но при этом подключен отдельным плагином англификатор Englification_Pack, который будет подменять основные русскоязычные текстовики игры англоязычными.
Так вот в таком случае нужно чтобы определяло именно английских язык. То есть проверка lod-архива игры или экзешника не подходит. Оптимально наверное проверять текстовики после загрузки их в память. Только как это лучше/правильней делать?

Насколько мне известно у HD-мода следующий порядок подрузки ресурсов:
- изначально загружаються файлы из lod-архивов из Data
- после те файлы, которые в Data лежат отдельными текстовиками, тем самым покрывая тектовики из lod-архива
- после загружаються файлы из Common
- после уже из подключенных плагинов

То есть если сортить по приоритету, то это будет в обратном порядке. Плагины кроют все, что было загружено до этого. Это как я понимаю.

-------------------------------------------------------------------------------------------
24) Есть несколько плагинов из темы с готовыми, которые если скачать готовую dll как бы у меня работают, но это понятно так как это уже собранный код. Но вот если я их собираю сам из предоставленного кода, то они не работают. Вот хотелось бы разобраться ПОЧЕМУ, в чем все таки причина.
В настройках компилятора или кривой библиотеке homm3.h. У меня выравнивание структур стоит в 1 байт, конфигурацию решения - "Release", homm3.h из первого сообщения данной темы.

Например, есть плагин, который дает герою возможность ежедневно восстанавливать ману в кол-ве: (1 + уровень_героя / 2):
------
Код: Выделить всё
int __stdcall Y_New_BaseRestoreMana(LoHook* h, HookContext* c)
{
   c->ebx += (int)(*(_word_*)(c->edi + 85) / 2);

   // c->ebx += (int)(((_Hero_*)c->edi)->level / 2);

   return EXEC_DEFAULT;
}
...
_PI->WriteLoHook(0x4E42D7, Y_New_BaseRestoreMana);


То что закоментированно было изначально от атвора. У меня собранный плагин с этим кодом не работал. В то время как код Ben80 из NewLearningSkill работал без каких-либо проблем. Ключевой момент в том, что Ben80 не использовал структуру _Hero_.
Переписав вот так: c->ebx += (int)(*(_word_*)(c->edi + 85) / 2) у меня все заработало.
------

И еще один - это Имущество с доходом 50/100/150 за каждый уровень героя:

Код: Выделить всё
int __stdcall newEstate(LoHook* h, HookContext* c)
{
   char skill_level = *(char*)(c->esi + 201 + 13); // current skill level
   _word_ hero_level = *(_word_*)(c->esi + 85);

    // _Hero_* hero = (_Hero_*)c->esi; // grab current hero structure
   // int skill_level = hero->second_skill[HSS_ESTATES]; // current skill level
   // int hero_level = hero->level;

   int gold = skill_level * hero_level * 50;
   c->eax = gold;
   return EXEC_DEFAULT;
}
...
_PI->WriteLoHook(0x4E4622, newEstate);


То что закоментированно было изначально и не работало. Переписал две строчки без структуры _Hero_ и все взлетело.
------

А вот этот по Мистицизму - 5/10/15% от макс кол-ва очков магии героя, так и не заработал:

Код: Выделить всё
int __stdcall NewMysticism(LoHook* h, HookContext* c)
{
   char skill_level = *(char*)(c->edi + 201 + 8);
   short maxSpellPoints = (short)(10 * (*(_byte_*)(c->edi + 1143)) * CALL_1(float, __thiscall, 0x4E4B20, c->edi));

   // _Hero_* hero = (_Hero_*)c->edi; // grab current hero structure
   // int skill_level = hero->second_skill[HSS_MYSTICISM]; // current skill level
   // int hero_level = hero->level;
   // short maxSpellPoints = (short)(10 * hero->knowledge * CALL_1(float, __thiscall, 0x4E4B20, hero)); // is only correct as long as knowledge is the only factor and not changed with other plugins
   
   int points = (int)(maxSpellPoints / 20 * skill_level);
   if (points <= 3)
      points = skill_level + 1; // get at least the normal mysticism values
   c->ebx = points; // in fact only this -1 is added by mysticism, the missing 1 is standard mana regeneration.
   c->return_address = 0x4E41CC;
   return NO_EXEC_DEFAULT;
}
...
_PI->WriteLoHook(0x4E41C5, NewMysticism);


Тут проблема либо вот в этом *(_byte_*)(c->edi + 1143), это я так получаю текущий уровень знаний героя, либо неверно передаю в CALL_1 последний аргумент - c->edi (у автора на этом месте была структурная переменная hero структуры _Hero_).
---
Но в любом случае это не решение каждый раз заниматься подобным. Должна быть возможность полноценно и без каких-либо проблем использовать все структуры homm3.h.

Мне важно понять почему так происходит. Ведь похожая проблема у меня уже наблюдалось и с палаткой с масхиллом и с кодом as239 по баллисте. Может действительно есть какие-то особенности в настройках/работе компилятора в VS 2008 и VS 2015 по умолчанию.

Ведь вам же, AlexSpl, тогда на VS 2008 не нужно было делать вот этого:
hero->second_skill[HSS_FIRST_AID] на *(char*)((int)hero + 201 + 27)

У вас итак все работало с той структрую из homm3.h, которая была изначально без каких-либо фиксов/выравниваний.
Когда у вас будет время если не сложно исправьте, пожалуйста, заголовочник homm3.h и перезалейте ее в первом сообщение, согласно вашему сообщению:

AlexSpl писал(а):

Нужно будет её как-нибудь "проапгрейдить": добавить известные поля и убрать проблему с выравниваем с помощью #pragma pack(push, 1).


Чтобы начинающие моддеры не ломали голову почему же их плагин не работает. Я тоже ее заодно перекачаю, чтобы полноценно использовать все структуры homm3.h без каких-либо проблем.
--------------------------------------------------------------------------------------------------
Выражаю благодарность и признательность AlexSpl за всю оказанную здесь помощь. В то же время хотел попросить по возможности, по свободе все же обратить внимание на перечисленные здесь плагины и помочь хотя бы исправить/доделать уже имеющиеся. Может быть чем-то смогли помочь и RoseKavalier с igrik, но они последнее время уж слишком редкие гости здесь.
Конечно было бы просто замечательно реализовать все или хотя бы большую часть из того, что здесь описано. Это бы уж точно пригодилось как начинающим моддерам, так и обычным игрокам, фанатам, как офлайнщикам, так и онлайнщикам. Но я прекрасно понимаю, что на все нужно время. Где был уже похожий код я его добавил в это сообщение, чтобы не тратить время на поиски и не набирать поновой. Конечно за те плагины, которые требуют немалых затрат времени, доп ислледования и много кода (как, например, 14 пункт), не каждый возьмется. Но мало ли может что-то действительно заинтересует и захочется любой ценой это реализовать. Хорошие вещи быстро не делаються. Таже Маскировка из темы с готовыми плагинами на мой взгляд является самым интересным плагином cреди всех выложенных в той теме альтернативы которому нет пока что даже у команды HotA. Но судя по первым страницам этой темы писался, переписывался, исправлялся и тестировался этот плагин долго и мучительно. Но оно того стоило.

Я же со совей стороны внимательно проштудировал все 186 страниц данной темы и собрал здесь все, на мой взгляд, самое необходимое, что еще не было реализовано отдельными плагинами для SoD/Complete. Что-то конечно есть в HotA (а чего-то нет даже и там), но сторонники чистого SoD/Complete (с некоторыми дополнениями, удобствами и правками баланса) совершенно незаслуженно лишены подобных плюшек.
Последний раз редактировалось Rolex 16 янв 2021, 14:36, всего редактировалось 30 раз(а).
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5547
Зарегистрирован: 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)
Поблагодарили: 2161 раз.

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

Сообщение AlexSpl » 09 ноя 2020, 03:49

По поводу структуры _Hero_. Действительно странно, что другие выравниваются нормально в VS 2015, а эта не хочет. Вот что пишет Microsoft:

Цитата:
The C++ standard doesn't specify packing behavior for alignment on boundaries smaller than the compiler default for the target platform, so you still need to use the Microsoft #pragma pack in that case.

Попробуйте "обернуть" структуру _Hero_:

Код: Выделить всё
#pragma pack(push, 1)
NOALIGN struct _Hero_
{
    ...
};
#pragma pack(pop)

Можно проделать это со всеми, начинающимися на NOALIGN.

* * *
Отвечу на простые вопросы.

Цитата:
6) Нужно уменьшить единицы передвижения, которые дают Перчатки всадника с 300 до 200, а те, которые дают Сапоги-скороходы с 600 до 400.

Т.к. значения загружаются из movement.txt, то пишем в afterInit():

*(int*)0x698B4C = 200; // Equestrian's Gloves (#70)
*(int*)0x698B50 = 400; // Boots of Speed (#98)

В гайде адреса для первичных бонусов. Там по четыре байта на каждый артефакт, по одному для Атаки, Защиты, Силы Магии, Знаний:
Цитата:
The primary attribute bonuses are stored in a table starting at 23E758, with
4 bytes per artifact - 1 each for Attack, Defense, Spell Power, and Knowledge, respectively.


Цитата:
А не подскажите адрес только теперь на файл ARRAYTXT.TXT ?

0x6A60AC.

Цитата:
18) Плагин, который даст возможность активировать героя через Shift + Левый Клик Мышки.

Код этого плагина должен быть в этой теме. Помню, его RoseKavalier ещё допиливал.

Цитата:
15) В оригинале есть небольшой баг, когда Забывчивость колдуется на все вражеские отряды уже на Продвинутом уровне магии воды, хотя это должно быть только на Эксперте. Есть ли где-то фикс этого бага или не подскажите как это исправить?

Посмотрите багфиксы Ben80 в теме с пользовательскими плагинами (возможно, там это исправлено).
Вернуться к началу

offlineBen80  
имя: Сергей
Эксперт
Эксперт
 
Сообщения: 1315
Зарегистрирован: 18 июн 2017, 06:49
Пол: Не указан
Поблагодарили: 336 раз.

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

Сообщение Ben80 » 09 ноя 2020, 15:34

Rolex писал(а):

15) В оригинале есть небольшой баг, когда Забывчивость колдуется на все вражеские отряды уже на Продвинутом уровне магии воды, хотя это должно быть только на Эксперте. Есть ли где-то фикс этого бага или не подскажите как это исправить?


Можешь здесь посмотреть download/file.php?id=1704
С Забывчивостью связан не один баг (а целых 3).
Это не тема плагинов к HD моду, а тема ASI плагинов к оригинальной игре. От HD мода на своем компьютере я, к счастью, избавился.
Вернуться к началу

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

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

Сообщение Rolex » 10 ноя 2020, 11:28

AlexSpl писал(а):

Попробуйте "обернуть" структуру _Hero_:

Да, спасибо, #pragma pack(push, 1) реально решило проблему.

AlexSpl писал(а):

*(int*)0x698B4C = 200; // Equestrian's Gloves (#70)
*(int*)0x698B50 = 400; // Boots of Speed (#98)

Все, 6-й готов. Спасибо.

AlexSpl писал(а):

0x6A60AC.

Что-то не получается. Нужно кое-какие данные из плагина изменить в ARRAYTXT.
Заменил #define o_ADVEVENT (*(_TXTEX_**)0x696A68) на
#define o_ARRAYTXT-> (*(_TXTEX_**)0x6A60AC)
Адрес afterInit не менял.
Тут понтяно: o_ARRAYTXT->SetString(номер_строки - 1, "наша_строка")
Что-то с этим:
inline void SetString(int index, char* str) { *(char**)(*(int*)((int)this + 32) + index * 4) = str; }
Размер структуры 4 байта. Видимо вместо 32 что-то другое должно быть, иначе не знаю в чем может быть причина.

AlexSpl писал(а):

Код этого плагина должен быть в этой теме. Помню, его RoseKavalier ещё допиливал.

Да, есть такое. Нашел, видимо проглядел. Вот ссылка на ваш код:
viewtopic.php?f=56&t=518&start=1280#p19353
На там курсор ведет себя странно. А вот в слеледующем сообщение от RoseKavalier 2 из 3 ссылкок уже недоступны.
Есть полная реализация:
https://github.com/RoseKavalier/H3Plugi ... hiftToggle
Не меняя структуры выкачал все файлы:
Зпустил ShiftToggle.vcxproj, но собрать dll, так и не получилось. Ошибки компиляции.
Выкачал отсюда: https://github.com/RoseKavalier/H3API/t ... er/include
H3API.hpp, H3API.natvis, H3Types.hpp + patcher_x86.hpp + всю папку h3api (с H3Allocator, H3Base и тд) и добавил в папку include к Hooks.h.
Подключил H3API.hpp (#include "H3API.hpp"), но ничего так и не заработало. Что мне нужно сделать, чтобы все скомпилировалось?
---
Заодно спрошу и за отображение расходуемых очков движения при зажатой клавише "Alt":
Вот здесь RoseKavalier писал что-то об этом (я не совсем его понимаю):
viewtopic.php?f=56&t=518&hilit=ALT&start=1230#p19281
Вот здесь вроде код уже почти готовый выкладывал, как его подправить, чтобы можно было собрать в dll без проблем?
viewtopic.php?f=56&t=518&hilit=ALT&start=1240#p19292
---

AlexSpl писал(а):

Посмотрите багфиксы Ben80 в теме с пользовательскими плагинами (возможно, там это исправлено).

В багфиксах бена, а также игрика и даже в SoD_SP это не исправленно.
Но, пожалуй, этот пункт для меня наименее важен. В приоритете сейчас первые 13-14 пунктов (ну и на 23 хотелось бы по возможности получить содержательный ответ). 14 хоть и интересный, но будет сложен в реализации, так что это так, если вдруг заинтересует.

По первому пункту несмотря на то, что много все понаписано, почти уже все решено. Нужно лишь помочь найти адрес на доп хук на 39 строку из GENRLTXT.TXT для стреляющих отрядов у которых остался 1 выстрел.

Rolex писал(а):

А вот эта проблема тянется еще из кода AlexSpl без дополнений as239 (эта проблема так и осталась неисправленной и в последней версии FreshMod 6.2; а вот SoD_SP этот момент учитывает). Если глянуть код:
Код: Выделить всё
                 _PI->WriteLoHook(0x493053, getLosses);
                 _PI->WriteLoHook(0x492619, showLosses);
                 _PI->WriteLoHook(0x492848, showLosses);

У нас три хука, первый высчитывает урон и кол-во убитых, два оставшихся добавляют вычесленное кол-во к оригинальным строкам из GENRLTXT.TXT.

Этот хук соответствует 38 строке в GENRLTXT.TXT - для обычной атаки:
Код: Выделить всё
_PI->WriteLoHook(0x492619, showLosses);

А этот 298 строке - для стрелковой атаки.
Код: Выделить всё
_PI->WriteLoHook(0x492848, showLosses);

Но проблема в том, что для лучников/баллисты у которых остался 1 выстрел, данные берутся не из 298 строки, а из 39 строки.
А доп хука на 39 строку у нас нет и, следовательно, когда у стреляющего отряда остается 1 выстрел, кол-во убитых не отображается. Отображается оригинальная строка без каких либо изменений.


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

using namespace std;

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

int MinDamage, MaxDamage, MinKilled, MaxKilled, FullHealth, shotsLeft, countCurrent;
string nameSingle, namePlural;
bool IsMelee;

int __stdcall getLosses(LoHook* h, HookContext* c)
{
   int DamageKt = 1;

   _BattleStack_* foeStack = *(_BattleStack_**)(c->ebp + 8);
   _BattleStack_* curStack = o_BattleMgr->GetCurrentStack();
   
   int foeStackID = foeStack->creature_id;
   int curStackID = curStack->creature_id;

   nameSingle = o_pCreatureInfo[foeStackID].name_single;
   namePlural = o_pCreatureInfo[foeStackID].name_plural;
   
   shotsLeft = curStack->creature.shots;
   countCurrent = foeStack->count_current;


   IsMelee = (*(int*)o_BattleMgr->Offset(0x132DC) == 7);

   // Проверка оставшегося кол-ва выстрелов. Удваивание урона для марксменов, грандов и баллисты только при наличии у них в запасе не менее 2-х выстрелов.
   if (shotsLeft > 1) {
      if ((curStackID == CID_MARKSMAN || curStackID == CID_GRAND_ELF) && (!IsMelee))
         DamageKt = 2;

      _Hero_* CurHero = o_BattleMgr->hero[o_BattleMgr->current_side];
      if (CurHero && curStackID == CID_BALLISTA) {
         int ARTI_Level = *(char*)((int)CurHero + 201 + 20);
         // int ARTI_Level = CurHero->second_skill[HSS_ARTILLERY];
         if (ARTI_Level == 2) DamageKt = 2;
         else if (ARTI_Level == 3) DamageKt = 4;
      }
   }
     
   MinDamage = DamageKt * c->edi;
   MaxDamage = DamageKt * c->eax;
   
   MinKilled = MinDamage / foeStack->full_hp;
   if (MinDamage % foeStack->full_hp >= foeStack->full_hp - foeStack->lost_hp) ++MinKilled;
   if (MinKilled > foeStack->count_current) MinKilled = foeStack->count_current;

   MaxKilled = MaxDamage / foeStack->full_hp;
   if (MaxDamage % foeStack->full_hp >= foeStack->full_hp - foeStack->lost_hp) ++MaxKilled;
   if (MaxKilled > foeStack->count_current) MaxKilled = foeStack->count_current;

   return EXEC_DEFAULT;
}

int __stdcall showLosses(LoHook* h, HookContext* c)
{
   string Buffer = o_TextBuffer;
   string NewBuffer, Name;

   // получаем имя вряжеского стека (если текущее кол-во юнитов в стеке больше 1, то в множественном числе, иначе в единственном числе).
   if (countCurrent > 1) Name = namePlural; else Name = nameSingle;

   // проверяем родной буфер на вхождение "damage", если оно есть, то формируем строку на англ языке, иначе на рус.
   if (Buffer.find("damage") != -1) {
      // Если у текущего отряда героя есть выстрелы, а также его атака не рукопашная, то есть в соседствующих гексах нет вражеского отряда, то формируем строку для стреляющего отряда, иначе для обычного (англ версия).
      if (shotsLeft > 0 && !IsMelee) NewBuffer = "Shoot " + Name + " (shots left: " + to_string(shotsLeft) + ", damage: "; else
                     NewBuffer = "Attack " + Name + " (damage: ";
   }
   else {
      // Если у текущего отряда героя есть выстрелы, а также его атака не рукопашная, то есть в соседствующих гексах нет вражеского отряда, то формируем строку для стреляющего отряда, иначе для обычного (рус версия).
      if (shotsLeft > 0 && !IsMelee) NewBuffer = "Выстрелить в отряд " + Name + " (осталось выстрелов: " + to_string(shotsLeft) + ", урон: "; else
                     NewBuffer = "Атаковать отряд " + Name + " (урон: ";
   }
   
   // если мин урон равен макс (под Благословением, например), то добавляем к нашей строке только один, иначе оба.
   if (MinDamage == MaxDamage) NewBuffer.append(to_string(MaxDamage)); else
                        NewBuffer.append(to_string(MinDamage) + '-' + to_string(MaxDamage));

   // переганяем стринг в массив чаров для корректного вывода.
   char* NewBufferWrite = new char[NewBuffer.size() + 1];
   strcpy(NewBufferWrite, NewBuffer.c_str());

   if (Buffer.find("damage") != -1) {
      if (MinKilled == MaxKilled) sprintf(o_TextBuffer, "%s, kills: %d)", NewBufferWrite, MaxKilled); else
                           sprintf(o_TextBuffer, "%s, kills: %d-%d)", NewBufferWrite, MinKilled, MaxKilled);
   }
   else {
      if (MinKilled == MaxKilled) sprintf(o_TextBuffer, "%s, убьет: %d)", NewBufferWrite, MaxKilled); else
                           sprintf(o_TextBuffer, "%s, убьет: %d-%d)", NewBufferWrite, MinKilled, MaxKilled);
   }

   return EXEC_DEFAULT;
}



// запрет на второй выстрел для баллисты и стрелков, если боезапас закончился (после первого выстрела в запасе 0)
int __stdcall Y_monstreShoot(LoHook* h, HookContext* c)
{
   _BattleStack_* mon = (_BattleStack_*)c->esi;

   if (mon->creature.shots < 1) {
      c->return_address = 0x43FFFC;
      return NO_EXEC_DEFAULT;
   }

   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.ShowLosses");

         // второй выстрел марксменами и грандами
         _PI->WriteLoHook(0x43FF92, Y_monstreShoot);

         // второй выстрел баллистой
         _PI->WriteLoHook(0x43FFF4, Y_monstreShoot);

         _PI->WriteLoHook(0x493053, getLosses);
         _PI->WriteLoHook(0x492619, showLosses);
         _PI->WriteLoHook(0x492848, showLosses);
      }
   }

   return TRUE;
}


По возможности конечно можно было бы еще добавить фикс, чтобы для юнитов на которых наложено Старение или Яд, урон и кол-во убитых считалось не по макс здоровью, а по сниженному.
А с крестоносцами и налетчиками на волках и заморачиваться не стоит.

По 2 и 3 там мне хотя бы показать на примере 2-3 заклов (функция(-ции)) + вызов) и подсказать с доп адресами если таковы понадобятся. Дальше уже буду сам по аналогии делать.

4,5, 7,8,9, 11 уже сделаны по большей части именно вами (кроме 4 и 11). Коды я предоставил, их лишь немного нужно исправить/дополнить.

10 пункт также полезен и вроде не сложен. Он есть в HotA, но для SoD/Complete я его не встречал. В чем его польза можно посмотреть на скрине.

Но особо меня интересуют 12, 13 пункты, подобного я еще нигде не встречал. Это были бы довольно интересные, и, главное, полезные плагины, которые должны быть не особо сложны в реализации (для опытных моддеров).

Необязательно за все сразу хвататься, постепенно по свободе. Это то, что действительно пригодится и будет полезным как многим игрокам, так и начинающим моддерам. Остальные же пункты для меня уже не столь значимы.
----------------------------------------------------
PS: AlexSpl, думаю, нашу Палатку с массхилом, а точнее сам навык Первая помощь, можно еще улучшить. Дело в том, что Палатка полезна лишь в начале игры на первых этапах. И чем дальше, тем меньше от нее пользы. И когда стреляющий вражеский отряд или все стреляющие отряды врага или те, которые к ней смогут добраться за один раунд и нанести суммарный урон больше, чем здоровье самой Палатки (плюс есть же огненный шар, метеоритный дождь, инферно, армагеддон), то все плюшки навыка Первая помощь из-за привязки к Палатке сводятся на нет.
Да, этот навык улучшает Палатку, но она по прежнему уязвима, а без нее никакой пользы от навыка Первой помощи больше нет. Нужно бы прокачивать не только саму Палатку, а еще и вторичный навык Первая помощь без привязки к Палатке.
Так некоторые моддеры добавляют +1, +2, +3 к здоровью всех отрядов в армии героя. Но, по-моему, это не серьезно, толку от этих 1-3 ед. крайне мало для юнитов более высоких уровней.

А потому предлагаю сделать для всех живых существ, то есть кроме нежити (включая нейтралов Мумий) и бездушных существ (големы (включая Золотых и Алмазных), горгульи, элементали и боевые машины) следующим образом:

Базовый уровень Первой Помощи: +5% от макс здоровья для всех живых существ, но не ниже 1.
Продвинутый уровень Первой Помощи: +10% от макс здоровья для всех живых существ, но не ниже 2.
Экспертный уровень Первой Помощи: +15% от макс здоровья для всех живых существ, но не ниже 3.

Сможете добавить это в ваш код выше? Можно будет потом как еще один альтернативный плагин выложить в готовых (как с Неудачей).
----------------------------------------------------

Ben80 писал(а):

Можешь здесь посмотреть download/file.php?id=1704

Спасибо. Но что-то с забывчивостью там ничего толком и не нашел. Зато нашел правку другого известного бага - гарпии против дендроидов.
Все таки было интересно проверить на основе какого экзешника мод создает свой через лаунчер (SoD 3.2 (eng) или Complete 4.0 (eng)). Все таки SoD 3.2 (eng).
Вот интересно, а генератор карт в 3.2 и 4.0 работает одинаково или все же в 4.0 есть какие-то улучшения/исправления?
ASI-плагины это конечно интересная тема, но как по мне это больше актуально для RoE/AB/Chronicles, а вот для SoD/Conplete, по-моему, удобней будет использовать плагины для мода. В любом случае мы используем patcher_x86.
Допустим, есть набор asi-плагинов (какое-то количество), так вот, например, игроку нужно подключить только часть из набора, так как подгружаться будут все, как ему быть? В таком случае другие ему придеться куда-то прятать в отдельную папку. Да и корневая папка будет засорятся кучей плагинов.
Как вариант, сделать так, чтобы при первом запуске asi-плагин создавал ini-файл со всеми входящими в него фиксами/плагинами, где каждый игрок сможет сам вкл или откл нужный плагин без необходимости прятать те, которые ему ненужны.
Хотя если в asi-плагин будет входить только лишь правка явных багов, без каких-либо плюшек/доп функционала, тогда да, это не потребуется.

***
----------------------------------------------------
UPD: Появились еще идеи для парочки интересных плагинов:

I.
После захвата внешнего жилища, чтобы в нем можно было нанимать не только обычных, а также и грейдженных юнитов, но только после того, как в самом городе будет отстроено улучшенное жилище для этого существа. Если же в городе эти юниты n-го уровня неулучшены, то и в жилище также. Иначе, после улучшения в городе, сделать окно найма этих существ во внешнем жилище в точности такое же как и в самом городе после улучшения. То есть чтобы кликом по картинке с существом можно было выбирать каких именно существ мы хотим нанять - улучшенных или неулучшенных. Если же среди всех подконтрольных герою городов нет таких существ, которые соответствуют захваченому внешнему жилищу, то выводить, как обычно, стандартное окно найма только с неулучшенными. Существ же первого уровня тогда соответственно сделать с окном найма на платной основе, чтобы также можно было выбирать каких именно мы хотим нанять (после улучшения их в городе).

II.
Переделать Консерваторию Грифонов и Улей Змиев в Сокровищницы ресурсов, со следующим бонусом:
50 грифонов: 3000 золота + 6 самоцветов (вместо 1 ангела)
100 грифонов: 6000 золота - 12 самоцветов (вместо 2 ангелов)
150 грифонов: 9000 золота - 18 самоцветов (вместо 3 ангелов)
200 грифонов: 12000 золота - 24 самоцвета (вместо 4 ангелов)

30 стрекоз: 2000 золота + 4 серы (вместо 4 выверн)
45 стрекоз: 3000 золота + 6 серы (вместо 6 выверн)
60 стрекоз: 4000 золота + 8 серы (вместо 8 выверн)
90 стрекоз: 6000 золота + 12 серы (вместо 12 выверн)
Последний раз редактировалось Rolex 07 авг 2021, 10:40, всего редактировалось 42 раз(а).
Вернуться к началу

offlineigrik  
Подмастерье
Подмастерье
 
Сообщения: 108
Зарегистрирован: 14 сен 2017, 12:35
Пол: Не указан
Поблагодарили: 84 раз.

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

Сообщение igrik » 10 ноя 2020, 11:37

Ben80 писал(а):

Rolex писал(а):

15) В оригинале есть небольшой баг, когда Забывчивость колдуется на все вражеские отряды уже на Продвинутом уровне магии воды, хотя это должно быть только на Эксперте. Есть ли где-то фикс этого бага или не подскажите как это исправить?


Можешь здесь посмотреть download/file.php?id=1704
С Забывчивостью связан не один баг (а целых 3).
Это не тема плагинов к HD моду, а тема ASI плагинов к оригинальной игре. От HD мода на своем компьютере я, к счастью, избавился.

Чёто у тебя в багофиксах я ничего не нашел про забывчивость. О каких трех багах ты говоришь?

Rolex писал(а):

Добавил также фикс igrik'a из солянки его кода, который запрещает второй выстрел для баллисты и стрелков с двумя выстрелами, если боезапас закончился. Это для тех случаев, когда в запасе остается только 1 выстрел и после первого уже нечем делать второй. Правда у баллисты декремент не рабоатет, это нужно фиксить.
Если без этого фикса, то гранды и марксмены после первого делают и второй, а счетчик оставшихся выстрелов уходит в минус. Выглядит так: 24(-1). Это однозначно баг, такого быть не должно.
И очень странно, но этот баг SOD_SP не исправляет (а вот HotA исправляет)...

А как ты видишь исправления багов не в солянке? На каждый баг отдельный плагин? Уж извольте :smile15:

Решение второго выстрела баллисты нужно делать тут 0x43FF9A
Код: Выделить всё
if ( stackAtt->creature_id == 146 && stacktarget->Count > 0 )
{
  // добавить проверку на ( stackAtt->creature.shoots > 0 )
}


Rolex, описанные тобой пункты потянут на целый год работы. Без обучения реверсу ты их не реализуешь. Нужно самому научиться находить адреса (а для этого нужно использовть и IDA и OLLY) и вставлять хуки (мало кто поможет тебе в таком объёме работы). Плюс нужно научиться решать проблему пересечения правок с HD и SOD_SP (а это вообще решать придется только тебе). Это трудно, но вполне возможно.

Что качается забывчивости, то у этого спелла просто отсутствует флаг 64 (имеет массовое воздействие на уровне эксперта)
Решение что-то в духе:
Код: Выделить всё
o_Spell[SPL_FORGETFULNESS].flags |= SPF_HAS_MASS_ON_EXPERT;


Rolex писал(а):

20) Плагин, который будет выводить остаток здоровья отряда (вместо максимального), во всплывающем по бокам, окне (с вкл опцией все данные в Сведениях о существах в Настройках боя).
У меня остался только плагин (который активно в ERA используется). Сам же код я каким-то образом потерял. А заново писать мне его было влом, да и смысла в этом нет.
https://dl.dropboxusercontent.com/s/mgp ... 0texts.dll

Rolex писал(а):

14) Добавление заклинаний

Эта задача практически неподъёмная. Нужно перелопатить полторы тонны кода, перенести пару таблиц и с десяток свитчей. Найти все обращения к ним, в том числе в коде ИИ. Это ахтунг. Но такое уже сделано одним человеком в одноимённом проекте (MoP - Master of puppets). И там всё на АСМе.

Rolex писал(а):

21) Плагин, который корректно снимет лимит на на количество капитолиев (в каждом городе должна быть возможность построить Капитолий).

Код: Выделить всё
_PI->WriteByte(0x4B9C62, 0);


Rolex писал(а):

23) А как можно наиболее правильно определить основной язык игры из плагина

Правильный ответ - никак.
В игре нет такого параметра. Ни HD мод, ни WOG, ни SOD_SP не определяет какой язык в игре.
В HD - в лаунчере сам выбираешь язык
В SOD_SP - в ini файле сам устанавливаешь язык
В WoG идет ориентировка на один текстовый файл. Но эта ориентировка только для русского и английского языков, и на другие не расчитана. Да и вообще эта ориентировка - паршивее некуда.
Можно придумать много способов "псевдо-определения" языка - но это всё один большой костыль. Все они будут выглядеть в духе: получить первый символ существа/артефакта/другого объекта и по нему определять язык.
Вернуться к началу

offlineBen80  
имя: Сергей
Эксперт
Эксперт
 
Сообщения: 1315
Зарегистрирован: 18 июн 2017, 06:49
Пол: Не указан
Поблагодарили: 336 раз.

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

Сообщение Ben80 » 10 ноя 2020, 12:41

igrik писал(а):

Чёто у тебя в багофиксах я ничего не нашел про забывчивость. О каких трех багах ты говоришь?


Код: Выделить всё
int __stdcall fixForgetfullnessDamage(LoHook* hook, HookContext* c)
{
    H3CombatMonster* battleStack = (H3CombatMonster*)c->ecx;
   
    if(CALL_2(bool, __thiscall, canShootProcAddress, battleStack, 0) == false)
        c->flags.ZF = 1;

    return EXEC_DEFAULT;
}
...
// fix wrong half damage
_PI->WriteLoHook(0x442E9B, fixForgetfullnessDamage);


Третий баг связан с ИИ. Если кому интересно, напишу подробнее.

***

Второй баг, фикс для которого я привел, описан Владимиром в справочнике на данном сайте. Стрелки в рукопашной били не в 2 раза слабее, а в 4.
Вернуться к началу

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

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

Сообщение Rolex » 10 ноя 2020, 13:00

igrik писал(а):

А как ты видишь исправления багов не в солянке? На каждый баг отдельный плагин? Уж извольте :smile15:

Я такого не говорил. Просто я взял этот отдельный фикс по стрелкам для плагина из первого пункта. А в 4-пункте собраны как раз именно фиксы всех багов связанных со стрельбой. Конечно, именно исправления багов лучше делать одним плагином.

igrik писал(а):

Rolex, описанные тобой пункты потянут на целый год работы. Без обучения реверсу ты их не реализуешь.

Довольно спорное утверждение. Это смотря что и как делать. Если заморачиваться с крестносцами и налетчиками с их вторым ударом, абилками существ, а также добавлением новых спеллов, то, возможно. Но я же отметил, что это сложно и не обязательно.

По сути нужны первые 13 пунктов из которых 6-й уже решен, 1-й почти тоже. Лишь нужен по сути один лоухук между двумя имеющимися _PI->WriteLoHook(0xADRESS, showLosses) для стрелков с 1 выстрелом в запасе. 4,5, 7,8,9, 11 тоже на 50-70% уже решены, их только немного исправить/дополнить. 2-3 пункты есть в HotA и SOD_SP, мне лишь пример с решением пары заклов и адреса, остальные, думаю, уже по аналогии и сам доделал бы. То есть по сути сделать нужно 10, 12, 13 пункты. Я могу конечно ошибаться, но как по мне, то для опытного моддера здесь максимум на полдня работы.

igrik писал(а):

Эта задача практически неподъёмная. Нужно перелопатить полторы тонны кода, перенести пару таблиц и с десяток свитчей. Найти все обращения к ним, в том числе в коде ИИ. Это ахтунг. Но такое уже сделано одним человеком в одноимённом проекте (MoP - Master of puppets). И там всё на АСМе.

А это случайно не Вызов светлячков? :smile12:
 
Изображение
Последний раз редактировалось Rolex 10 ноя 2020, 19:42, всего редактировалось 4 раз(а).
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5547
Зарегистрирован: 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)
Поблагодарили: 2161 раз.

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

Сообщение AlexSpl » 10 ноя 2020, 15:39

Цитата:
Что-то не получается. Нужно кое-какие данные из плагина изменить в ARRAYTXT.

Адрес правильный, но хук afterInit() не подойдёт, т.к. эта игра быстренько :smile2: копирует массив строк в другой массив. Нам нужно успеть изменить оригинальный массив строк до копирования.

Код: Выделить всё
#define o_ADVEVENT (*(_TXTEX_**)0x696A68)
#define o_ARRAYTXT (*(_TXTEX_**)0x6A60AC)

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)
{
   o_ADVEVENT->SetString(171, "{Witch's Hut}\n\nYour text #1 here.");
   o_ADVEVENT->SetString(172, "{Witch's Hut}\n\nYour text #2 here.");
   o_ADVEVENT->SetString(173, "{Witch's Hut}\n\nYour text #3 here.");
   o_ADVEVENT->SetString(190, "{Witch's Hut}\n\nYour text #4 here.");
   
   return EXEC_DEFAULT;
}

int __stdcall afterArrayTxtLoad(LoHook* h, HookContext* c)
{
   o_ARRAYTXT->SetString(2, "{Attack} Your description here.");
   
   return EXEC_DEFAULT;
}

...

_PI->WriteLoHook(0x4EE1C1, afterInit);
_PI->WriteLoHook(0x5BA046, afterArrayTxtLoad);
Вернуться к началу

Пред.След.

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

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

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

cron