Объявления

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

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

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

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

Сообщение Rolex » 30 июл 2021, 18:42

AlexSpl писал(а):

Когда кнопка апгрейда неактивна, попап диалог не показывается? Или в чём сложность?

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

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

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

Сообщение AlexSpl » 30 июл 2021, 18:47

Я дописал предыдущий пост. Универсальное и практичное решение, мне кажется. Перехватываем все правые нажатия на юнитах и обновляем данные в строке ресурсов игрока, после закрытия диалога возвращаем ресурсы игрока.
Вернуться к началу

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

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

Сообщение Rolex » 30 июл 2021, 18:54

Я не знаю верно ли мы друго друга поняли. А зачем нам вообще трогать строку ресурсов игрока? Я там картинку в своем сообщении постил. Там где я дописал на картинке ресурсы, вот я за те места вел речь. Когда кнопка диалога неактивна никакой диалог мы не откроем.
Последний раз редактировалось Rolex 30 июл 2021, 19:16, всего редактировалось 2 раз(а).
Вернуться к началу

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

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

Сообщение AlexSpl » 30 июл 2021, 18:57

Тогда выводите стоимость грейда в статусной строке просто (там, где "Select Unit"/"View Unit"). Зачем сложности? :smile2: Расчёт стоимости грейда можете взять из моего плагина, который показывает действия игрока AI (там через операторы).
Последний раз редактировалось AlexSpl 30 июл 2021, 19:03, всего редактировалось 1 раз.
Вернуться к началу

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

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

Сообщение Rolex » 30 июл 2021, 19:03

То есть дописывать в вспылвающее сообщение по ПКМ по этой кнопке нашу строку будет проблематично?
Изображение
Ведь по клику правой клавишей мыши всплывающее сообщение выводиться всегда независимо от того активна кнопка или нет. Просто хотел сделать так, как сделано в HotA.
Вернуться к началу

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

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

Сообщение AlexSpl » 30 июл 2021, 19:06

Да всё можно сделать. Просто какой смысл запихивать инфу так далеко?

Вот код, из которого можете взять подсчёт стоимости грейда:

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

#define DEBUGL1
//#define DEBUGL2

Patcher* _P;
PatcherInstance* _PI;

short hiredIdx = ID_NONE;

// For debugging purposes
void showDebugInfo(const char* format, ...)
{
    va_list args;
    va_start(args, format);

    vsprintf(o_TextBuffer, format, args);
    b_MsgBox(o_TextBuffer, MBX_OK);

    va_end(args);
}

void showInfo(const _Army_* army, const _Hero_* hero, const int action, const bool upgradeDone = false)
{
    char txtBuffer[1024];
    char infoStr[1024];

    if ( action != ID_NONE )
        sprintf(txtBuffer, "Action: {%d}", action);
    else
        if ( upgradeDone )
            sprintf(txtBuffer, "{%s}", "AFTER");
        else
            sprintf(txtBuffer, "{%s}", "BEFORE");

    sprintf(infoStr, "Player: {%d}  |  Hero: {%s}  |  %s\n\n", o_ActivePlayer->id, hero ? hero->name : "N/A", txtBuffer);
   
    for (int i = 0; i < 7; ++i)
    {
        if ( army->type[i] != ID_NONE )
            sprintf(txtBuffer, "{%s}: %d\n", o_pCreatureInfo[army->type[i]].name_plural, army->count[i]);
        else
            sprintf(txtBuffer, "%s\n", "[Empty]");
        strcat(infoStr, txtBuffer);
    }
   
    showDebugInfo("%s\nWood: {%d} | Ore: {%d}\nMercury: {%d} | Sulfur: {%d} | Crystal: {%d} | Gems: {%d}\nGold: {%d}",
        infoStr, o_ActivePlayer->resourses.wood, o_ActivePlayer->resourses.ore, o_ActivePlayer->resourses.mercury,
        o_ActivePlayer->resourses.sulfur, o_ActivePlayer->resourses.crystal, o_ActivePlayer->resourses.jems,
        o_ActivePlayer->resourses.gold);
}

int getCreatureBasicType(const int id)
{
    int basicType = id;
   
    if ( id % 2 && (id < CID_AIR_ELEMENTAL || id == CID_SPRITE || id == CID_MAGIC_ELEMENTAL || id == CID_PHOENIX) )
        basicType = id - 1;
    else
    switch ( id )
    {
    case CID_STORM_ELEMENTAL:
        basicType = CID_AIR_ELEMENTAL;
        break;
    case CID_MAGMA_ELEMENTAL:
        basicType = CID_EARTH_ELEMENTAL;
        break;
    case CID_ENERGY_ELEMENTAL:
        basicType = CID_FIRE_ELEMENTAL;
        break;
    case CID_ICE_ELEMENTAL:
        basicType = CID_WATER_ELEMENTAL;
        break;
    }

    return basicType;
}

int getCreatureUpgradeType(const int id)
{
    return CALL_1(int, __fastcall, 0x47AAD0, id);
}

_Dwelling_* GetDwelling(const int id) {
    return (_Dwelling_ *)(o_GameMgr->Field<int>(0x4E39C) + 92 * id);
}

int __stdcall initDwelling(LoHook* h, HookContext* c)
{
    _Dwelling_* dwelling = (_Dwelling_*)c->esi;
    int upgMonId = getCreatureUpgradeType(dwelling->creature_types[0]);
   
    if ( dwelling->type == 0x11 && upgMonId != ID_NONE )
    {
        dwelling->creature_types[1] = dwelling->creature_types[0];
        dwelling->creature_counts[1] = dwelling->creature_counts[0];
        dwelling->creature_types[0] = upgMonId;
        if ( dwelling->defenders.type[0] != ID_NONE )
            dwelling->defenders.type[0] = upgMonId;
    }

    return EXEC_DEFAULT;
}

void __stdcall fixDwellingDefenders(HiHook* h, _Dwelling_* dwelling, bool unused)
{
    CALL_2(void, __thiscall, h->GetDefaultFunc(), dwelling, unused);

    if ( dwelling->type == 0x11 )
    {
        dwelling->defenders.type[1] = ID_NONE;
        dwelling->defenders.count[1] = 0;
    }
}

short getObjectType(const _Hero_* hero)
{
    short objType = ID_NONE;

    if ( o_ActivePlayer->selected_hero_id != -1 && hero )
    {
        _MapItem_* item = o_GameMgr->Map.GetItem(hero->x, hero->y, hero->z);
        if ( item ) objType = item->object_type;
    }

    return objType;
}

_Dwelling_* getDwelling(const _Hero_* hero)
{
    _Dwelling_* dwelling = 0;
   
    if ( hero )
    {
        _MapItem_* item = o_GameMgr->Map.GetItem(hero->x, hero->y, hero->z);
        if ( item && (item->object_type == 0x11 || item->object_type == 0x14) ) dwelling = GetDwelling(item->setup);
    }

    return dwelling;
}

int __stdcall afterHiring(LoHook* h, HookContext* c)
{
    _Dwelling_* dwelling = 0;
   
    if ( o_ActivePlayer->selected_hero_id != ID_NONE )
        dwelling = getDwelling(&o_GameMgr->hero[o_ActivePlayer->selected_hero_id]);
   
    if ( dwelling && dwelling->type == 0x11 )
    {
        dwelling->creature_counts[0] = min(dwelling->creature_counts[0], dwelling->creature_counts[1]);
        dwelling->creature_counts[1] = dwelling->creature_counts[0];

        if ( h->GetAddress() == 0x5510B1 )
        {
            c->return_address = 0x55121B;
            return NO_EXEC_DEFAULT;
        }
    }

    return EXEC_DEFAULT;
}

struct AIStackToRecruit
{
    int creatureType;
    short* sourceArmyCounts;
    short creaturesAvailable;
    char r1;
    char r2;
    void updateAvailableCreatures();
} *AIStacksToRecruit = new AIStackToRecruit[16];

struct _ResourcesEx_ : public _Resources_
{
    friend const _ResourcesEx_ operator + (const _ResourcesEx_& a, const _ResourcesEx_& b);
    friend const _ResourcesEx_ operator - (const _ResourcesEx_& a, const _ResourcesEx_& b);
    friend _ResourcesEx_ operator += (_ResourcesEx_& a, const _ResourcesEx_& b);
    friend _ResourcesEx_ operator -= (_ResourcesEx_& a, const _ResourcesEx_& b);
    friend const _ResourcesEx_ operator * (const int n, const _ResourcesEx_& r);
    friend const _ResourcesEx_ operator * (const _ResourcesEx_& r, const int n);
    friend bool operator >= (const _ResourcesEx_& a, const _ResourcesEx_& b);
};

const _ResourcesEx_ operator + (const _ResourcesEx_& a, const _ResourcesEx_& b)
{
    _ResourcesEx_ result;

    result.wood = a.wood + b.wood;
    result.ore = a.ore + b.ore;
    result.mercury = a.mercury + b.mercury;
    result.sulfur = a.sulfur + b.sulfur;
    result.crystal = a.crystal + b.crystal;
    result.jems = a.jems + b.jems;
    result.gold = a.gold + b.gold;

    return result;
}

const _ResourcesEx_ operator - (const _ResourcesEx_& a, const _ResourcesEx_& b)
{
    _ResourcesEx_ result;

    result.wood = a.wood - b.wood;
    result.ore = a.ore - b.ore;
    result.mercury = a.mercury - b.mercury;
    result.sulfur = a.sulfur - b.sulfur;
    result.crystal = a.crystal - b.crystal;
    result.jems = a.jems - b.jems;
    result.gold = a.gold - b.gold;

    return result;
}

_ResourcesEx_ operator += (_ResourcesEx_& a, const _ResourcesEx_& b)
{
    a.wood += b.wood;
    a.ore += b.ore;
    a.mercury += b.mercury;
    a.sulfur += b.sulfur;
    a.crystal += b.crystal;
    a.jems += b.jems;
    a.gold += b.gold;

    return a;
}

_ResourcesEx_ operator -= (_ResourcesEx_& a, const _ResourcesEx_& b)
{
    a.wood -= b.wood;
    a.ore -= b.ore;
    a.mercury -= b.mercury;
    a.sulfur -= b.sulfur;
    a.crystal -= b.crystal;
    a.jems -= b.jems;
    a.gold -= b.gold;

    return a;
}

const _ResourcesEx_ operator * (const int n, const _ResourcesEx_& r)
{
    _ResourcesEx_ result;

    result.wood = n * r.wood;
    result.ore = n * r.ore;
    result.mercury = n * r.mercury;
    result.sulfur = n * r.sulfur;
    result.crystal = n * r.crystal;
    result.jems = n * r.jems;
    result.gold = n * r.gold;

    return result;
}

const _ResourcesEx_ operator * (const _ResourcesEx_& r, const int n)
{
    _ResourcesEx_ result;

    result.wood = n * r.wood;
    result.ore = n * r.ore;
    result.mercury = n * r.mercury;
    result.sulfur = n * r.sulfur;
    result.crystal = n * r.crystal;
    result.jems = n * r.jems;
    result.gold = n * r.gold;

    return result;
}

bool operator >= (const _ResourcesEx_& a, const _ResourcesEx_& b)
{
    return a.gold >= b.gold &&
        a.mercury >= b.mercury && a.sulfur >= b.sulfur && a.crystal >= b.crystal && a.jems >= b.jems &&
        a.wood >= b.wood && a.ore >= b.ore;
}

void AIStackToRecruit::updateAvailableCreatures()
{
    *this->sourceArmyCounts = this->creaturesAvailable;
   
    // Если грейд, а справа - его базовое существо, уравниваем кол-во существ, доступных для найма
    // Пользуемся тем, что после грейда всегда идёт его базовое существо
    if ( this[1].creatureType == getCreatureBasicType(this->creatureType) )
    {
        *this[1].sourceArmyCounts = this->creaturesAvailable;
        this[1].creaturesAvailable = this->creaturesAvailable;
    }

    // Если базовое существо, а слева - его грейд, уравниваем кол-во существ, доступных для найма
    if ( hiredIdx && this->creatureType == getCreatureBasicType(this[-1].creatureType) )
    {
        *this[-1].sourceArmyCounts = this->creaturesAvailable;
        this[-1].creaturesAvailable = this->creaturesAvailable;
    }
}

// Грейдим отряды при посещении города
int __stdcall AI_UpgradeCreatures(LoHook* h, HookContext* c)
{
    _Hero_* hero = (_Hero_*)c->ecx;
    _Town_* town = (_Town_*)c->edx;
   
    bool upgradeDone = false;

    if ( hero && town )
    {
        _Army_* army = (_Army_*)&hero->army;

#ifdef DEBUGL1
        showInfo(&hero->army, hero, ID_NONE);
#endif

        _ResourcesEx_* playerResources = (_ResourcesEx_*)&o_ActivePlayer->resourses;

        for (int j = 0; j < 7; ++j)
        {
            int maxValue = -1;
            int bestCreatureIdx = ID_NONE;

            for (int i = 0; i < 7; ++i)
            {
                if ( army->type[i] != ID_NONE && army->type[i] == getCreatureBasicType(army->type[i]) &&
                    o_pCreatureInfo[army->type[i]].town == town->type && town->IsBuildingBuilt(37 + o_pCreatureInfo[army->type[i]].level, 1) &&
                    o_pCreatureInfo[army->type[i]].fight_value > maxValue )
                {
                    maxValue = o_pCreatureInfo[army->type[i]].fight_value;
                    bestCreatureIdx = i;
                }
            }
               
            if ( bestCreatureIdx != ID_NONE )
            {
                _ResourcesEx_* creatureCost = (_ResourcesEx_*)&o_pCreatureInfo[army->type[bestCreatureIdx]].cost;
                _ResourcesEx_* upgradedCreatureCost = (_ResourcesEx_*)&o_pCreatureInfo[getCreatureUpgradeType(army->type[bestCreatureIdx])].cost;

                if ( *playerResources >= army->count[bestCreatureIdx] * (*upgradedCreatureCost - *creatureCost) )
                {
                    *playerResources -= army->count[bestCreatureIdx] * (*upgradedCreatureCost - *creatureCost);
                    army->type[bestCreatureIdx] = getCreatureUpgradeType(army->type[bestCreatureIdx]);
                    upgradeDone = true;
                }
            }
        }

#ifdef DEBUGL1
        if ( upgradeDone ) showInfo(&hero->army, hero, ID_NONE, true);
#endif
    }

    return EXEC_DEFAULT;
}

int __stdcall AI_TryingToHire(LoHook* h, HookContext* c)
{
    AIStackToRecruit* stacksBegin = *(AIStackToRecruit**)(c->edi + 0x30);
    AIStackToRecruit* stacksEnd = *(AIStackToRecruit**)(c->edi + 0x34);

    // Сохраняем оригинальные указатели
    AIStackToRecruit* stacksBeginSaved = stacksBegin;
    AIStackToRecruit* stacksEndSaved = stacksEnd;

    _Army_* army = *(_Army_**)(c->ebp + 8);
    _Hero_* hero = army ? (_Hero_*)((int)army - 0x91) : 0;
    _Army_* garrisonArmy = *(_Army_**)(c->ebp + 0x10);
    _ResourcesEx_* playerResources = *(_ResourcesEx_**)(c->ebp + 0x14);
   
    int action = 0;
   
    if ( stacksBegin )
    {
        int stackPos = 0;

        for (AIStackToRecruit* iStack = stacksBegin; iStack < stacksEnd; ++iStack)
        {
            AIStacksToRecruit[stackPos] = *iStack;

            int basicType = getCreatureBasicType(AIStacksToRecruit[stackPos].creatureType);
            int objType = getObjectType(hero);
            if ( AIStacksToRecruit[stackPos].creatureType != basicType && objType != 0x11 && objType != 0x14 && objType != 0x4E )
            {
                AIStacksToRecruit[stackPos + 1] = AIStacksToRecruit[stackPos];
                AIStacksToRecruit[stackPos + 1].creatureType = basicType;
                ++stackPos;
            }
            ++stackPos;
        }

        stacksBegin = &AIStacksToRecruit[0];
        stacksEnd = &AIStacksToRecruit[stackPos];

        // Подменяем оригинальный вектор существ для найма
        *(AIStackToRecruit**)(c->edi + 0x30) = stacksBegin;
        *(AIStackToRecruit**)(c->edi + 0x34) = stacksEnd;
       
#ifdef DEBUGL1
        int stacksNumber = stacksEnd - stacksBegin;
       
        char txtBuffer[1024] = "";
        char infoStr[1024];
       
        sprintf(infoStr, "Trying to hire {%d} stack%s:\n\n", stacksNumber, stacksNumber > 1 ? "s" : "");

        for (AIStackToRecruit* iStack = stacksBegin; iStack < stacksEnd; ++iStack)
        {
            sprintf(txtBuffer, "{%s}\n", o_pCreatureInfo[iStack->creatureType].name_plural);
            strcat(infoStr, txtBuffer);         
        }

        showDebugInfo(infoStr);
       
        // Показываем армию и ресурсы до найма
        if ( army ) showInfo(army, hero, action++);
#endif
    }
   
    int value;
    do
    {
        // Пытаемся нанять отряды
        value = CALL_2(int, __thiscall, 0x42D420, c->edi, c->esi);
        if ( value > 0 && hiredIdx != ID_NONE )
        {
            stacksBegin[hiredIdx].updateAvailableCreatures();
            hiredIdx = ID_NONE;

#ifdef DEBUGL1
            if ( army ) showInfo(army, hero, action++);
#endif
        }
    }
    while (value > 0);
                       
    // Восстанавливаем оригинальный вектор существ для найма,
    // чтобы игра могла корректно освободить выделенную память
    *(AIStackToRecruit**)(c->edi + 0x30) = stacksBeginSaved;
    *(AIStackToRecruit**)(c->edi + 0x34) = stacksEndSaved;
   
    c->return_address = 0x42D768;
    return NO_EXEC_DEFAULT;
}

int __stdcall AI_ThinkingOfHiring(LoHook* h, HookContext* c)
{
    AIStackToRecruit* stacksBegin = *(AIStackToRecruit**)(c->esi + 0x30);
    AIStackToRecruit* stacksEnd = *(AIStackToRecruit**)(c->esi + 0x34);
     
#ifdef DEBUGL2
    if ( stacksBegin )
    {
        int stacksNumber = stacksEnd - stacksBegin;
       
        char txtBuffer[1024] = "";
        char infoStr[1024];
       
        sprintf(infoStr, "Thinking of hiring {%d} stack%s:\n\n", stacksNumber, stacksNumber > 1 ? "s" : "");

        for (AIStackToRecruit* iStack = stacksBegin; iStack < stacksEnd; ++iStack)
        {
            sprintf(txtBuffer, "{%s}\n", o_pCreatureInfo[iStack->creatureType].name_plural);
            strcat(infoStr, txtBuffer);         
        }

        showDebugInfo(infoStr);
    }
#endif

    c->edx = *(int*)(c->ebp + 8);
    int value;
    do
    {
        value = CALL_2(int, __thiscall, 0x42D420, c->esi, 0);
        c->edx += value;
    }
    while (value > 0);
       
    c->return_address = 0x42D864;
    return NO_EXEC_DEFAULT;
}

int __stdcall AI_HiringBestCreaturesFirst(LoHook* h, HookContext* c)
{
    AIStackToRecruit* stacksBegin = *(AIStackToRecruit**)(c->esi + 0x30);
    int maxValue = *(int*)(c->ebp - 0xC);
    int stackIdx = *(int*)(c->ebp - 0x18);
    int stackType = *(int*)(c->ebp - 0x20);
       
    int fightValue = o_pCreatureInfo[stackType].fight_value;
    c->edi = fightValue;
   
    return EXEC_DEFAULT;
}

int __stdcall AI_SaveHiredCreatureIdx(LoHook* h, HookContext* c)
{
    hiredIdx = *(short*)(c->ebp - 0x18);

    return EXEC_DEFAULT;
}

int __stdcall skipMessages(LoHook* h, HookContext* c)
{
    if ( _P->VarGetValue("HD.UI.AdvMgr.SkipMapMsgs", 1) )
    {
        c->return_address = 0x4A1917;
        return NO_EXEC_DEFAULT;
    }

    return EXEC_DEFAULT;
}

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

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

            _PI->WriteLoHook(0x4A17BD, skipMessages);
            _PI->WriteLoHook(0x4B85EE, initDwelling);
            _PI->WriteLoHook(0x5510B1, afterHiring);
            _PI->WriteHexPatch(0x4A197B, "90 90");
            _PI->WriteHexPatch(0x4AB812, "90 90");
            _PI->WriteHiHook(0x4B8760, SPLICE_, EXTENDED_, THISCALL_, fixDwellingDefenders);
           
            // AI
            _PI->WriteLoHook(0x42D71C, AI_TryingToHire);
            _PI->WriteLoHook(0x42D84F, AI_ThinkingOfHiring);
            _PI->WriteLoHook(0x42D67B, AI_SaveHiredCreatureIdx);
            _PI->WriteLoHook(0x42D768, afterHiring);
            _PI->WriteHexPatch(0x42CC06, "90 90 90");

            // AI Experiments
            _PI->WriteLoHook(0x42D57D, AI_HiringBestCreaturesFirst);
            _PI->WriteLoHook(0x4AADBE, AI_UpgradeCreatures);
            _PI->WriteLoHook(0x4AAF40, AI_UpgradeCreatures);
        }
    }

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

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

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

Сообщение Rolex » 30 июл 2021, 19:10

AlexSpl писал(а):

Просто какой смысл запихивать инфу так далеко?

Дело в том, что в статусной строке в окне существа мало места, да и шрифт мелкий. А в окне по ПКМ там и шрифт крупнее и места побольше, что хорошо для больших сумм и существ, которые требуют ресурсов. Поэтому лучше чтобы было и там и там.

***

Тогда нужна Ваша помощь с адресами хуков.
Вернуться к началу

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

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

Сообщение AlexSpl » 30 июл 2021, 19:16

Самое сложное, наверное, адреса для хуков. Но если однажды разобраться, поймёте, что это не rocket science, как говорят наши друзья. Попробуйте всё-таки начать с брейкпоинтов. Я вообще сейчас в Disciples III играю (5-й акт за Легионы; сложная миссия, кстати: например, некромантов в самом начале, по-моему, нереально победить).

В общем, максимально точно опишите, что хотите получить на выходе, а я, как будет время, займусь. Но мне не очень интересно писать готовый код, гораздо приятнее видеть, когда человек с моей помощью написал плагин. Есть во мне что-то от педагога :smile1: Типа хобби такое - помогать. Плагины я бы писал за деньги, если этим можно было зарабатывать. А так понимаешь, что ничему не учишься и крадёшь время, которое мог бы потратить на игрушки :smile1:

* * *
Лирика ahead...

Мне всегда хотелось видеть результат. Я не из тех, кто будет скрупулёзно читать книжку "С++ для чайников" или "IDA для чайников", на первом месте у меня мотивация. Именно она, если есть, позволит не только догнать "книжного червя", но и перегнать его. Думаете, современные джуниоры, которые хвастаются скиллом, разбираются, что за синтаксисом плюсов? Как это всё работает? Думаю, нет. А если да, то это уже перспективные джуниоры. Через тернии к звёздам (per aspera ad astra), через ошибки к опыту. Это раньше был какой-то душевный подъём оттого, что понимал asm (мама, смотри что я могу). Сейчас понимаешь, что и asm'ов много, и вообще не в них корень. Мотивация и ещё раз она! Этим Герои и хороши. Они - неисчерпаемый источник мотивации :smile1: То, что мы делаем, можно причислить к хакерскому искусству. И приложений у патчера больше, чем может показаться. Таких бы знаний всем джуниорам.
Вернуться к началу

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

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

Сообщение Rolex » 30 июл 2021, 20:10

AlexSpl писал(а):

Я вообще сейчас в Disciples III играю (5-й акт за Легионы; сложная миссия, кстати: например, некромантов в самом начале, по-моему, нереально победить).

Эх, и где Вы только время на все находите и на семью и на игрушки. Целую кучу игр уже прошли. Я читал темы с вашим прохождением и Героев, и других игр. А в Доту или КС не играете?

AlexSpl писал(а):

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

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

AlexSpl писал(а):

Есть во мне что-то от педагога :smile1: Типа хобби такое - помогать.

Ну, видимо, помогая другим Вы получаете от этого какое-то моральное удовлетрение.

AlexSpl писал(а):

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

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

AlexSpl писал(а):

В общем, максимально точно опишите, что хотите получить на выходе, а я, как будет время, займусь.

Если есть под рукой HotA, можете глянуть, как там сделано. Вот что-то наподобие этого. Вот как на скрине, который я постил на предыдущей странице. То есть отображение (дописывание к оригинальному тексту в статусной строке) необходимого кол-ва ресурсов для грейда стека в статусной строке при наведении курсора мыши на кнопку апгрейда. А также по клику правой кнопкой мыши на кнопке апгрейда в всплывающем окошке.
Последний раз редактировалось Rolex 30 июл 2021, 20:28, всего редактировалось 2 раз(а).
Вернуться к началу

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

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

Сообщение AlexSpl » 30 июл 2021, 20:16

Цитата:
Ну, видимо, помогая другим Вы получаете от этого какое-то моральное удовлетрение.

Вы правы, но не такое, как раньше. И от Героев тоже. Всё-таки прав был Дима (VDV_forever): переписывает нас возраст. Жесть.

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

Когда заранее видишь результат, совсем не интересно. У меня в "мысленном будущем" этот плагин уже в теме пользовательских. Просто каждый раз задаёшь себе вопрос: зачем? Ну, ладно, этот точно последний. Думаю, Ben80 меня бы понял.

* * *
Я вот Вам удивляюсь, Rolex. Неужели Вы и вправду играете с этими плагинами? Мейнстрим сейчас - HotA. Не поймите меня неправильно, просто хотелось бы знать природу Вашего энтузиазма. На df2 есть пользователи, которые генерируют идею гигабайтами в день в надежде, что они обретут жизнь в следующем релизе HotA, но я спешу разочаровать таких. А вот в плагинах их идеи могли бы жить. Но как и было раньше (преклонение перед авторитетом: NWC; HotA Crew), так и будет. И я понимаю причину: хочется, чтобы "в твою идею" играл не только ты. Этого, увы, я обеспечить не могу :smile1:
Последний раз редактировалось AlexSpl 30 июл 2021, 20:28, всего редактировалось 1 раз.
Вернуться к началу

Пред.След.

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

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

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