• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

[OT Client] Custom Modal Windows

whitevo

Feeling good, thats what I do.
Joined
Jan 2, 2015
Messages
3,452
Solutions
1
Reaction score
625
Location
Estonia
So I'm trying to understand OTC, but o boy this is complicated piece of code.
So though maybe I could start from 1 feature at the time.

First thing what probably is most interesting to learn is Modal Windows.
How do you make custom ones? Can I use LUA codes for it?
How receive data from TFS?
Can I return tables? Or the window:sendToPlayer(player) is all we can do?

Now how can I manipulate the received data to change the modal window panel shape and such?
Can I just put any UI pictures there? Also I don't want to make all the windows look the same.
Quite the opposite, I want every modal window to be custom.

Where do I write my code in first place? I create new modul?

Do I have to compile after changing the pictures and ccs like code?
 
So I'm trying to understand OTC, but o boy this is complicated piece of code.
So though maybe I could start from 1 feature at the time.

First thing what probably is most interesting to learn is Modal Windows.
How do you make custom ones? Can I use LUA codes for it?
How receive data from TFS?
Can I return tables? Or the window:sendToPlayer(player) is all we can do?

Now how can I manipulate the received data to change the modal window panel shape and such?
Can I just put any UI pictures there? Also I don't want to make all the windows look the same.
Quite the opposite, I want every modal window to be custom.

Where do I write my code in first place? I create new modul?

Do I have to compile after changing the pictures and ccs like code?
First question: I suggest the game_modaldialog folder - Sidenote, it's "Lua" not "LUA" ;)
Second question: it receives all the info in window which is metadata. It sends the window object, containing all the buttons, text, choices, etc. to the player and to the client and the client displays it.
Third question: what do you mean by return tables?
Fourth question: see answer to first question
Fifth question: I would assume it's possible, but see answer to fourth question
Sixth question: I guess I'd have to recommend seeing the answer to the fifth question
 
First question: I suggest the game_modaldialog folder - Sidenote, it's "Lua" not "LUA" ;)
Second question: it receives all the info in window which is metadata. It sends the window object, containing all the buttons, text, choices, etc. to the player and to the client and the client displays it.
Third question: what do you mean by return tables?
Fourth question: see answer to first question
Fifth question: I would assume it's possible, but see answer to fourth question
Sixth question: I guess I'd have to recommend seeing the answer to the fifth question
Do I have to edit modaldialog.lua to handle different modalWindowID's or I make a new LUA file for each custom modal window I want to have?
You didn't really answer my questions you just said where the current modalwindow.lua file is.

What are these .otmod and .otui files for?

I found this when I was trying to figure out how to make the modal window base panel look different. Is .otui some kind of image loader to memory?
Code:
bool UIManager::importStyle(std::string file)
{
    try {
        file = g_resources.guessFilePath(file, "otui");

        OTMLDocumentPtr doc = OTMLDocument::parse(file);

        for(const OTMLNodePtr& styleNode : doc->children())
            importStyleFromOTML(styleNode);
        return true;
    } catch(stdext::exception& e) {
        g_logger.error(stdext::format("Failed to import UI styles from '%s': %s", file, e.what()));
        return false;
    }
}
Anyway.. I see no direct link between the images and the modal window file.
How do I change the pictures?
What file/function I'm looking for, because doesn't look like the modaldialog.lua is right one. Seems to handle only the logic.

I want to do something like this:
Code:
if id == 2007 then
modalDialog = g_ui.createWidget('ModalDialog', skillTreeBG)
else
modalDialog = g_ui.createWidget('ModalDialog', rootWidget)
end
and so on for other UI elements: skillTreebuttons, choices, etc, etc.
 
What are these .otmod and .otui files for?
otui files are similar to css files you find on a webpage, they control the layout of the user interface hence the name otui.. (open tibia user interface)

If I were looking for a way to altar the layout of the interface i would start in the directory with the images, this would allow me to pinpoint exactly where these files were loaded.
https://github.com/edubart/otclient/tree/1686d8081b7462caebb11fd8b6d38be721f2c45e/data/images/ui

Just doing a search on 1 of these files including its parent directory for say ui/item-blessed
Gives me this link as a result.
https://github.com/edubart/otclient...c45e/modules/game_inventory/inventory.otui#L3
 
OK I did something (made otui and otmod file) and have this skilltree.lua with well nothing important right now.
But how do I activate the module when I open modalWindow 2007 in game?
 
As there is no reason why id 2007 should have a special modal window you should maybe think about using a custom packet / extended opcode for that.
e.g. player:sendSkillTree() sending a new packet in ProtocolGame or an extended opcode with the required data in the buffer.

Other than that make sure your ui file is imported (g_ui.importStyle('modaldialog')) and then create a widget of your new style using g_ui.createWidget.
 
As there is no reason why id 2007 should have a special modal window you should maybe think about using a custom packet / extended opcode for that.
e.g. player:sendSkillTree() sending a new packet in ProtocolGame or an extended opcode with the required data in the buffer.

Other than that make sure your ui file is imported (g_ui.importStyle('modaldialog')) and then create a widget of your new style using g_ui.createWidget.
sounds like i need to edit source for that.

Is there no other way?
 
sounds like i need to edit source for that.

Is there no other way?

Editing the source isn't a terrible thing. But I can see you aren't comfortable with it, so moving on...


Like the person above said, there are ways to get around editing the source when making new UI Windows. (I haven't used opcodes yet, because I haven't started working on new UI Windows, I just edit the current ones and the OTClient layout)

The problem is sending the OTClient all the information it needs. And it also depends on how custom the UI Windows will be and what function they will perform.
If you are making a Skill Tree, it has been done many times before (not by me) but I'm sure other people can tell you how they did it.

I doubt they used Modal Windows for it at all.
 
Editing the source isn't a terrible thing. But I can see you aren't comfortable with it, so moving on...


Like the person above said, there are ways to get around editing the source when making new UI Windows. (I haven't used opcodes yet, because I haven't started working on new UI Windows, I just edit the current ones and the OTClient layout)

The problem is sending the OTClient all the information it needs. And it also depends on how custom the UI Windows will be and what function they will perform.
If you are making a Skill Tree, it has been done many times before (not by me) but I'm sure other people can tell you how they did it.

I doubt they used Modal Windows for it at all.
Im not against copy pasting the entire talent config table to OTC lua file too.
Only choice id and button id matters to figure out which skill was chosen.

currently searching about that extended opcode thing
 
Only choice id and button id matters to figure out which skill was chosen.

Actually that is pretty simple. Use for, in for use createwidget, assign id to label/button, and then use label.onClick = function(self) some action end. I have used that way in store_module and it works like it should.
 
Ok. So whats up with the extended opcode? How do I send it? Where do I send it and Why do I send it again?

player:sendExtendedOpcode(player, 10, "test")
player:sendExtendedOpcode(10, "test")
Above code do absolutely nothing.
I tried to catch by registering new creaturescript:
Code:
function onExtendedOpcode(player, opcode, buffer)
    print(opcode)
    print(buffer)
end

Anyway, that aside. This is the LUA file I did for skillTree. I still can't figure out the way to test it and see what does it look like or does it even work.
Its the combination of the tutorial found from the OTC github place and the modalWindow.lua file.
Code:
Skilltree = {}

skilltreeWindow = nil
selecetedTalent = nil
skilltree = {}
ProtocolGame.registerExtendedOpcode(10, test)

function init()
    g_ui.importStyle('modaldialog')
    connect(g_game, { onOpenskilltreeWindow = Skilltree.create, onGameEnd = Skilltree.destroy })
end

function terminate()
    disconnect(g_game, { onOpenskilltreeWindow = Skilltree.create, onGameEnd = Skilltree.destroy })
    Skilltree.destroy()
    Skilltree = nil
end

function Skilltree.create(skillList, id, title, message, buttons, enterButton, escapeButton, choices, priority)
    print("skillList "..tostring(skillList))
    print("id "..tostring(id))
    print("title "..tostring(title))
    print("message "..tostring(message))
    print("buttons "..tostring(buttons))
    print("enterButton "..tostring(enterButton))
    print("escapeButton "..tostring(escapeButton))
    print("choices "..tostring(choices))
    print("priority "..tostring(priority))
   
skilltree = skillList
    Skilltree.destroy()

skilltreeWindow = g_ui.displayUI('skilltree.otui')
text = g_ui.createWidget('ModalDialog', rootWidget)
local messageLabel = text:getChildById('messageLabel')
local choiceList = text:getChildById('choiceList')
local choiceScrollbar = text:getChildById('choiceScrollBar')
local buttonsPanel = text:getChildById('buttonsPanel')
local labelHeight
local buttonsWidth = 0
local additionalHeight = 0

    text:setText(title)
    messageLabel:setText(message)

    for i = 1, #choices do
        local choiceId = choices[i][1]
        local choiceName = choices[i][2]
        local label = g_ui.createWidget('ChoiceListLabel', choiceList)
        label.choiceId = choiceId
       
        label:setText(choiceName)
        label:setPhantom(false)
        if not labelHeight then
            labelHeight = label:getHeight()
        end
    end
   
    choiceList:focusNextChild()

    for i = 1, #buttons do
        local buttonId = buttons[i][1]
        local buttonText = buttons[i][2]
        local button = g_ui.createWidget('ModalButton', buttonsPanel)
       
        button:setText(buttonText)
       
        button.onClick = function(self)
            local focusedChoice = choiceList:getFocusedChild()
            local choice = 0xFF
           
            if focusedChoice then
                choice = focusedChoice.choiceId
            end
           
            g_game.answerModalDialog(id, buttonId, choice)
            destroyDialog()
        end
       
        buttonsWidth = buttonsWidth + button:getWidth() + button:getMarginLeft() + button:getMarginRight()
    end
   
    if #choices > 0 then
        choiceList:setVisible(true)
        choiceScrollbar:setVisible(true)

        additionalHeight = math.min(text.maximumChoices, math.max(text.minimumChoices, #choices)) * labelHeight
        additionalHeight = additionalHeight + choiceList:getPaddingTop() + choiceList:getPaddingBottom()
    end

    local horizontalPadding = text:getPaddingLeft() + text:getPaddingRight()
    buttonsWidth = buttonsWidth + horizontalPadding

    text:setWidth(math.min(text.maximumWidth, math.max(buttonsWidth, messageLabel:getWidth(), text.minimumWidth)))
    messageLabel:setWidth(math.min(text.maximumWidth, math.max(buttonsWidth, messageLabel:getWidth(), text.minimumWidth)) - horizontalPadding)
    text:setHeight(text:getHeight() + additionalHeight + messageLabel:getHeight() - 8)

    local enterFunc = function()
        local focusedChoice = choiceList:getFocusedChild()
        local choice = 0xFF
       
        if focusedChoice then
            choice = focusedChoice.choiceId
        end
       
        g_game.answerModalDialog(id, enterButton, choice)
        destroyDialog()
    end

    local escapeFunc = function()
        local focusedChoice = choiceList:getFocusedChild()
        local choice = 0xFF
       
        if focusedChoice then
            choice = focusedChoice.choiceId
        end
       
        g_game.answerModalDialog(id, escapeButton, choice)
        destroyDialog()
    end

    choiceList.onDoubleClick = enterFunc

    text.onEnter = enterFunc
    text.onEscape = escapeFunc
end

function Skilltree.destroy()
    if skilltreeWindow then
        skilltreeWindow:destroy()
        skilltreeWindow = nil
        selecetedTalent = nil
        skilltree = {}
    end
end

function Skilltree.selectSkill()
    if table.empty(skilltree) then
        return
    end
-- TODO
end
 
In function init add that line with ProtocolGame.register~, in function terminate add ProtocolGame.unregister~.
Create function test(protocol, opcode, buffer) as it is the name assigned to opcode in init function.

From the server side use:
In extendedopcode.lua
player:sendExtendedOpcode([51-99], "test")
Opcode must be in range from 51-99 as from 1-50 and from 100-??? are used by some default functions.
 
The function test which you are registering is not declared anywhere.
Code:
function test(protocol, opcode, buffer)
  print(buffer)
end
 
Editing the source isn't a terrible thing. But I can see you aren't comfortable with it, so moving on...


Like the person above said, there are ways to get around editing the source when making new UI Windows. (I haven't used opcodes yet, because I haven't started working on new UI Windows, I just edit the current ones and the OTClient layout)

The problem is sending the OTClient all the information it needs. And it also depends on how custom the UI Windows will be and what function they will perform.
If you are making a Skill Tree, it has been done many times before (not by me) but I'm sure other people can tell you how they did it.

I doubt they used Modal Windows for it at all.
How do i edit the sources? Where is it? Need to decompile it or something?
 
Back
Top