Wake of Gods Forum | Форум Во Имя Богов

Full Version: ERA III
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
@Berserker

Minor issue.

We should align parameter type 'VersionLine' in 'ReportPluginVersion' method. In GameExt.pas type is string and in Extern.pas it is pchar.
Exported version use pchar and it is already used in few plugins.
Maybe we can use explicit cast in GameExt to avoid confusion and keep parameter types as pchar.

https://github.com/ethernidee/era/blob/3...t.pas#L156
https://github.com/ethernidee/era/blob/3...n.pas#L242
gamemaster, all exported functions use pchar (const char*) type for string. Automatic conversion is performed by Delphi when passing pchar to functions, expecting AnsiString/WideString, etc. I see no issue here. Am I wrong?

procedure ShowMessage (Mes: pchar); stdcall;
function Ask (Question: pchar): TDwordBool; stdcall;
I did not check other exported methods. If same principle is used at other places then it is ok to leave it as-is.
There is no issue with current implementation.
gamemaster, Delphi 12 rules. Will be used for 4.x releases.
--------------
Нарыл макет расширенного диалога радиокнопок, который хотели реализовать. Для памяти:
Image: image.png
https://dropmefiles.com/IgdED

Предполагалась возможность навигации и выбора в том числе с клавиатуры, а реакция на кнопки полностью на стороне клиентского кода. Например, выбор из большого каталога монстров или артефактов.
Исходник, когда-то написанного кода под этот макеет. По факту, 70% работы уже сделано) Но я уже наврят-ли буду этим заниматься. Так что при желании - любой может продолжить

Code:
#define _H3API_PATCHER_X86_
#include "../../Headers/H3API.hpp"
#include "era.h"
#include "era dialogs.h"

using namespace h3;
using namespace NH3Dlg;
using namespace EraDlg;

#define FUN

Patcher* _P;
PatcherInstance* _PI;

//CHAR myString[1024];
//#define MyString (PCHAR)myString

const char* CHECKBOX_DEF = "checkbox.def";
const char* RADIOBTN_DEF = "radiobttn.def";

#define RGB_FRAME_COLOR 0x78D4E8

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

* Шрифт 15 пикс, поддержка картинок через разметку Эры вида {~>....} до 44 пикс (реально и до 50 пикс).

* Один экран — до 8 пунктов.
* Щелчок по любому пространству между разделителями активирует пункт.
* Помещаются все слоты героя или 8 уровней жилищ города.
* Вмещаются дефы артефактов и трёхстрочные тексты пунктов/заголовка

* По клавишам 1-8 можно выбрать любой пункт (для галочек вкл/выкл).
* Стрелки влево, вправо, вверх активируют соответствующие кнопки. Enter/ESC тоже.
* Шрифт medfont.fnt или times08r.fnt. Пока нет поддержки нормальных шрифтов фиксированных размеров.
* Если подтвердим макет, напишу тогда программную спецификацию: структуру инициализации и нужные функции для экспорта.

* CTRL клавишу отслеживаем при щелчках по кнопкам или текущей позиции в постраничной навигации.
* CTRL + Лево будет на первую,
* CTRL + право — на последнюю.

* Home - первая страница,
* End — последняя.
* Вверх — на уровень вверх.
* "A" — выделить все, или, если все уже выделены, снять выделение со всех (в режиме галочек)
*/

// создание процесса диалога
INT32 __stdcall igrikDlgProc(H3Dlg* dlg, H3Msg* msg)
{  
    if(msg->IsLeftDown() && msg->flags == H3Msg::MessageFlag::MF_Null && msg->item_id >= 10 && msg->item_id <= 18)
    {
        H3DlgDefButton* button = (H3DlgDefButton*)dlg->GetH3DlgItem(msg->item_id +10);

        if (button)
            button->Press(msg);
    }


    if (msg->IsRightClick())
    {
        sprintf(MyString, "itemID: %d", msg->item_id);
        F_MsgBox(MyString, 4);
    }

    return dlg->DefaultProc(msg);
}


INT32 __stdcall showSelectDialog(TSelectDlgConfig* dlgConfig)
{
    ////////////////////////////////////
    //
    // базовые координаты и параметры диалога
    //
    ////////////////////////////////////
    INT32 width = 440;
    INT32 height = 548;

    INT32 startX = 24;
    INT32 startY = 88;

    INT32 deltaY = 50;    
    INT32 itemWidth = width - (startX*2);

    H3Dlg dlg(width, height, -1, -1, FALSE, igrikDlgProc);

    
    /////////////////////////////////////////////////////////
    //
    // дополнительные элементы: ОК, ОТМЕНА и другие элементы
    //
    ////////////////////////////////////////////////////////

    // заголовок (id: 3)
    dlg.CreateText(startX, 22, itemWidth, 62, dlgConfig->caption, Text::MEDIUM, TextColor::REGULARY, 3, TextAlignment::MiddleCenter, 0);
    // dlg.CreateFrame(dlg.GetH3DlgItem(3), RGB_FRAME_COLOR, 100, 1);

    // кнопка навигации влево (id: 4)
    if ( dlgConfig->flags & TSelectDlgFlags::SELECT_DLG_LEFT_BTN )
        dlg.CreateButton(width/2 -72, height -52, 44, 44, 4, "ADAG.DEF", 15, 40, 0, NH3VKey::H3VK_LEFT);
    // кнопка навигации враво (id: 5)
    if ( dlgConfig->flags & TSelectDlgFlags::SELECT_DLG_RIGHT_BTN )
        dlg.CreateButton(width/2 +28, height -52, 44, 44, 5, "ADAG.DEF", 11, 36, 0, NH3VKey::H3VK_RIGHT);  
    // кнопка навигации вверх (id: 6)
    if ( dlgConfig->flags & TSelectDlgFlags::SELECT_DLG_UP_BTN )
        dlg.CreateButton(width/2 -22, height -52, 44, 44, 6, "ADAG.DEF", 9, 34, 0, NH3VKey::H3VK_UP);

    // текст отображения страниц "1/3" (id: 9)
    if ( dlgConfig->flags & TSelectDlgFlags::SELECT_DLG_PAGE_NAV ) {
        sprintf(MyString, "%d/%d", dlgConfig->page, dlgConfig->numPages);
        dlg.CreateText(width -60, height -48, 40, 30, MyString, Text::SMALL, TextColor::REGULAR, 9, TextAlignment::MiddleCenter, 0);
    }
    // Cancel-рамка (id: 30626) и Cancel-кнопка (id: 30726)
    if ( dlgConfig->flags & TSelectDlgFlags::SELECT_DLG_CANCEL_BTN )
        dlg.CreateCancelButton(width -70 -64, height -52);
    // Ok-рамка (id: 30625) и Ok-кнопка (id: 30725)
    if (dlgConfig->flags & TSelectDlgFlags::SELECT_DLG_REQUIRE_ITEM )
         dlg.CreateOKButton(60, height -52)->Disable();
    else dlg.CreateOKButton(60, height -52);

    /////////////////////////////////////////////////////////
    //
    // тексты      (id: 10...18)
    // кнопки      (id: 20...28)
    // разделители (id: 0)
    //
    ////////////////////////////////////////////////////////

    dlg.CreateLineSeparator(startX-1, startY -1, itemWidth);
    dlg.CreateLineSeparatorYellow(startX, startY, itemWidth);

    for (int i = 0; i < dlgConfig->numItems; i++)
    {            
        dlg.CreateLineSeparator(startX-1, deltaY*(i+1) +startY -1, itemWidth);
        dlg.CreateLineSeparatorYellow(startX, deltaY*(i+1) +startY, itemWidth);

        sprintf(MyString, "{~>un44.def:%d valign=middle} itemCaptions[%d]: Each is null-terminated string.", 250+i, i);
        dlg.CreateText(50, deltaY*i + startY , itemWidth -26, deltaY, MyString,
            Text::SMALL, TextColor::REGULAR, 10+i, TextAlignment::MiddleLeft, 0);

        // dlg.CreateFrame(50, deltaY*i + startY, itemWidth -26, deltaY, 0xFFD700);
        H3DlgDefButton* b = dlg.CreateButton(startX, deltaY*i +startY + 18, 16, 16, /* id */ 20+i, Assets::RADIOBTN_DEF, 0, 1, 0, 2+i);
    }

    // стартуем диалог (после выполнения уничтожается автоматически)
    dlg.Start();

    return 1;
}


/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
#ifdef FUN

void ShowNewDialog()
{

    // создаём структуру диалога
    TSelectDlgConfig* dlgConfig = new TSelectDlgConfig;

    dlgConfig->caption = "Выберите монстра, которого вы ненавидели с детства, которого не любила Ваша мама, Ваш брат и Ваша собака. Выберите его и принесите в жертву Одину.";
    dlgConfig->isMultiple = false;
    dlgConfig->flags = 31;
    dlgConfig->numItems = 8;

    dlgConfig->page = 1;
    dlgConfig->numPages = 8;

    //dlgConfig->itemCaptions[0] = "text: itemCaptions[0]";    
    //dlgConfig->itemStates[0] = 1;

    showSelectDialog(dlgConfig);
    
    // очищаем выделенную память ...
    delete dlgConfig;
}


INT32 __stdcall Y_AdvMgr_KeyDown(HiHook* h, H3AdventureManager* advMgr, H3Msg* msg, INT32 x, INT32 y, INT32 ptrIsHuman)
{
    INT32 result = THISCALL_5(INT32, h->GetDefaultFunc(), advMgr, msg, x, y, ptrIsHuman);
    if (result)
    {
        switch(msg->subtype)
        {
            case NH3VKey::H3VK_5:
                // F_sprintf("count: %d \n type: %d", mon->count, mon->type);
                // F_MessageBox("Start TSelectDlgResult");
                    ShowNewDialog();
                break;

            default:
                break;
        }
    }
    return result;
}

INT32 __stdcall Y_DlgMainMenu_Proc(HiHook* hook, H3Msg* msg)
{
    if ( msg->IsKeyDown())
    {
        if ( msg->subtype == NH3VKey::H3VK_T )
        {
            F_VideoPause();
            ShowNewDialog();
            F_VideoContinue();
        }
    }
    return THISCALL_1(INT32, hook->GetDefaultFunc(), msg);
}

#endif FUN

/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////

void HooksInit()
{
#ifdef FUN
    // нажатие клавиши на карте приключений
    _PI->WriteHiHook(0x4FBDA0, SPLICE_, EXTENDED_, THISCALL_, Y_DlgMainMenu_Proc);
    _PI->WriteHiHook(0x4089DC, CALL_, EXTENDED_, THISCALL_, Y_AdvMgr_KeyDown);
#endif FUN
}

/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////

const char* PLUGIN_NAME = "era dialogs";

BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    static BOOL pluginOn = FALSE;
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
        if (!pluginOn)
        {
            _P = GetPatcher();
            _PI = _P->CreateInstance(PLUGIN_NAME);

            HooksInit();
        }
        break;

    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

Скрин (Click to View)
igrik, отлично, спасибо! )

А клавиши нельзя в обработчике событий диалога ловить? Обязательно для каждого диалога делать новый перехват?
(11.04.2024 05:20)Berserker Wrote: [ -> ]А клавиши нельзя в обработчике событий диалога ловить? Обязательно для каждого диалога делать новый перехват?
Так они в обработчике событий диалога и описаны (чтобы при нажатии на линии с текстом, отрабатывало нажатие чекбоксов/радиобаттонов). Где ты увидел перехват? Или я не понял сути твоего вопроса.
Quote:void HooksInit()
{
#ifdef FUN
    // нажатие клавиши на карте приключений
    _PI->WriteHiHook(0x4FBDA0, SPLICE_, EXTENDED_, THISCALL_, Y_DlgMainMenu_Proc);
    _PI->WriteHiHook(0x4089DC, CALL_, EXTENDED_, THISCALL_, Y_AdvMgr_KeyDown);
#endif FUN
}
Так это тестовый запуск диалога на время написания/создания. События же в Эре ещё пока такого нет, для запуска и инициализации диалога, а тестировать как-то да надо.
В нормальном режиме нужно будет твоё событие для вызова. И конфигурационная настройка тоже твоя)))

А запуск, я ожидал, что будет происходить как-то так:
Code:
extern "C" __declspec(dllexport) int ShowSelectDialog(TSelectDlgConfig* dlgConfig);
Но не мне уже этим заниматься))
(10.04.2024 17:53)Berserker Wrote: [ -> ]gamemaster, Delphi 12 rules. Will be used for 4.x releases.

Dance3 Nice.
Maybe Archer30 can add modern era to Heroes3 Launcher for beta testing.
It would be good idea to have bat file to switch between original and modern era dlls in case that problem occur.
More peoples can test it and find potential port problems that way. That will provide more reliable base for 4.x releases.

Berserker, if you have any new code for 3.x push it to git so I can include it into port.
gamemaster, IP:S/M commands are on approach. I'll update git as new release is ready. Btw, seems like VFS have problems with Wine, need to install and debug on spare time.

I think you can just send updated dlls to daemon_n on discord, so that he used them in Launcher without switcher.
gamemaster, updated VFS on github. Single character bug. The fix restores Wine's functionality and ExaGear, probably.
Nice to see new functionality coming. I hope you soon start with 4.x Sm

Ported Vfs change to modern era. Now era and vfs is up to date with original git.

Should we include "Chinese_Rainbow_Plugin" into modern era repository or keep it separate?
Actually we can extract it, it was just an easies way to keep it up to date and sources non abandoned. Seems like Chinese players use nowadays another plugin. Archer will probably explain this part better.
Quote:Seems like Chinese players use nowadays another plugin.
wogcn developed by gu7979gu is the only Chinese language plugin being used in ERA. There's another C++-based plugin being developed for H3 in general, but the author doesn't show strong interest in extending the support to ERA.
Reference URL's