• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!

How can I create buttons in the AllianceSlot frames?

Level 24
Joined
Jun 26, 2020
Messages
1,853
Hello, I wanna add buttons in the Allies Menu, specifically in the AllianceSlot frames to make it more dynamic and not having to create another custom menu that fills the screen, the thing is the alliance slot are labeled from 0 to 23 but they are not asigned to their respective player with that id (I mean the AllianceSlot_0 is not necessarly asigned to the Player 0), they are asigned based on the order their ids but excluding the local player, (it means that for example for the player 2 the AllianceSlot 0 and 1, will be asigned to the players 0 and 1 respectively but the AllianceSlot 2 is asinged to the Player 3 and the AllianceSlot 3 for the Player 4 and so on, because the player 2 is not visible in the allies menu of the player 2).

I made this script to correctly asign them and it worked, but when I click them I cause a desync, and I have understood that a frame can have a different parent or frame points between players without a desync, can you tell me what should I do?
Lua:
Debug.beginFile("SpectAlly")
OnInit.final(function ()
    --Require "FrameLoader"
    --Require "PlayerUtils"

    local slots = {}

    --FrameLoaderAdd(function ()
        BlzFrameClick(BlzGetFrameByName("UpperButtonBarAlliesButton", 0))
        BlzFrameClick(BlzGetFrameByName("AllianceCancelButton", 0))

        for i = 0, bj_MAX_PLAYER_SLOTS - 1 do
            -- To create their handles
            BlzGetFrameByName("AllianceSlot", i)
            BlzGetFrameByName("PlayerNameLabel", i)

            local p = Player(i)
            if GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(p) == MAP_CONTROL_USER then
                slots[p] = __jarray(bj_MAX_PLAYER_SLOTS - 1)
                local s = 0
                for j = 0, bj_MAX_PLAYER_SLOTS - 1 do
                    local p2 = Player(j)
                    if GetPlayerSlotState(p2) == PLAYER_SLOT_STATE_PLAYING then
                        if p ~= p2 then
                            slots[p][p2] = s
                            s = s + 1
                        end
                    end
                end
            end
        end

        for i = 0, bj_MAX_PLAYER_SLOTS - 1 do
            local p = Player(i)
            if GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING --[[and GetPlayerController(p) == MAP_CONTROL_USER]] then
                print(slots[GetLocalPlayer()][p])
                local slot = BlzGetFrameByName("AllianceSlot", slots[GetLocalPlayer()][p])
                local label = BlzGetFrameByName("PlayerNameLabel", slots[GetLocalPlayer()][p])

                local text = BlzCreateFrame("ScriptDialogButton", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), 0, 0)
                BlzFrameSetParent(text, slot)
                BlzFrameSetPoint(text, FRAMEPOINT_TOPRIGHT, label, FRAMEPOINT_TOPRIGHT, -0.01, 0.005)
                BlzFrameSetPoint(text, FRAMEPOINT_BOTTOMRIGHT, label, FRAMEPOINT_BOTTOMRIGHT, -0.01, -0.005)
                BlzFrameSetPoint(text, FRAMEPOINT_LEFT, label, FRAMEPOINT_RIGHT, -0.06, 0.)
                BlzFrameSetText(text, "See")
                BlzFrameSetTextAlignment(text, TEXT_JUSTIFY_CENTER, TEXT_JUSTIFY_MIDDLE)
                BlzFrameSetLevel(text, 4)
                BlzFrameSetVisible(text, GetLocalPlayer() ~= p)

                local t = CreateTrigger()
                BlzTriggerRegisterFrameEvent(t, text, FRAMEEVENT_CONTROL_CLICK)
                TriggerAddAction(t, function ()
                    print(GetPlayerName(p))
                end)
            end
        end
    --end)

    local t = CreateTrigger()
    TriggerRegisterPlayerChatEvent(t, Player(0), "r ", false)
    TriggerAddAction(t, function ()
        local sl = tonumber(GetEventPlayerChatString():sub(3))
        RemovePlayer(Player(sl), PLAYER_GAME_RESULT_DEFEAT)
    end)
end)
Debug.endFile()
 
I suggest always create the additional button & Triggers and the buttons directly as childs of alliance slot [0 to max player slot -1], seperate it from the interpration or is playing logic which you put into trigger action.

Touching AllianceDialog with frameapi breaks replays for this map.
if you want to support replays for your map, you need to add one fake handle (Location(0,0) for each alliance frame you touch in replay mode .
 
Last edited:
only matters when you want to keep replay working.

during replay BlzGetFrameByName("AllianceSlot", i) returns no frame making all other handles mismatch, therefore at that point the replay will desync. To counter that you create an replacement Handle in repay mode. for example a Location.

Triggerhappy made a spell system to know if the game currently is a replay.
 
Level 24
Joined
Jun 26, 2020
Messages
1,853
Ok, I edited the system to this and seems fine and even added a trigger that hides the respective button when the player leaves (I needed to add another system):
Lua:
OnInit("GameStatus", function ()
    ---@enum GameStatus
    GameStatus = {
        UNKNOWN = 0,
        ONLINE = 1,
        OFFLINE = 2,
        REPLAY = 3
    }

    local status = GameStatus.UNKNOWN ---@type GameStatus

    ---@return GameStatus
    function GameStatus.get()
        return status
    end

    ---@param p player
    ---@return boolean
    function IsPlayerInGame(p)
        return GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(p) == MAP_CONTROL_USER
    end

    local firstPlayer = Player(0)
    while not IsPlayerInGame(firstPlayer) do
        firstPlayer = Player(GetPlayerId(firstPlayer) + 1)
    end

    -- Force the player to select a dummy unit
    local u = CreateUnit(firstPlayer, FourCC('hfoo'), 0, 0, 0)
    SelectUnit(u, true)
    local selected = IsUnitSelected(u, firstPlayer)
    RemoveUnit(u)

    if selected then
        -- Detect if replay or offline game
        if (ReloadGameCachesFromDisk()) then
            status = GameStatus.OFFLINE
        else
            status = GameStatus.REPLAY
        end
    else
        -- If the unit wasn't selected instantly, the game is online
        status = GameStatus.ONLINE
    end
 
end)
Lua:
Debug.beginFile("SpectAlly")
OnInit.final(function ()
    --Require "FrameLoader"
    --Require "PlayerUtils"
    Require "GameStatus"

    local slots = {}

    --FrameLoaderAdd(function ()
        BlzFrameClick(BlzGetFrameByName("UpperButtonBarAlliesButton", 0))
        BlzFrameClick(BlzGetFrameByName("AllianceCancelButton", 0))

        for i = 0, bj_MAX_PLAYER_SLOTS - 1 do
            -- To create their handles
            if GameStatus.get() == GameStatus.ONLINE then
                BlzGetFrameByName("AllianceSlot", i)
                BlzGetFrameByName("PlayerNameLabel", i)
            else
                Location(0, 0)
                Location(0, 0)
            end

            local p = Player(i)
            if IsPlayerInGame(p) then
                slots[p] = __jarray(Player(bj_MAX_PLAYER_SLOTS - 1))
                local s = 0
                for j = 0, bj_MAX_PLAYER_SLOTS - 1 do
                    local p2 = Player(j)
                    if GetPlayerSlotState(p2) == PLAYER_SLOT_STATE_PLAYING then
                        if p ~= p2 then
                            slots[p][s] = p2
                            s = s + 1
                        end
                    end
                end
            end
        end

        local leave = CreateTrigger()

        for i = 0, bj_MAX_PLAYER_SLOTS - 1 do
            local slot = BlzGetFrameByName("AllianceSlot", i)
            local label = BlzGetFrameByName("PlayerNameLabel", i)

            local button = BlzCreateFrame("ScriptDialogButton", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), 0, 0)
            BlzFrameSetParent(button, slot)
            BlzFrameSetPoint(button, FRAMEPOINT_TOPRIGHT, label, FRAMEPOINT_TOPRIGHT, -0.01, 0.005)
            BlzFrameSetPoint(button, FRAMEPOINT_BOTTOMRIGHT, label, FRAMEPOINT_BOTTOMRIGHT, -0.01, -0.005)
            BlzFrameSetPoint(button, FRAMEPOINT_LEFT, label, FRAMEPOINT_RIGHT, -0.06, 0.)
            BlzFrameSetText(button, "See")
            BlzFrameSetTextAlignment(button, TEXT_JUSTIFY_CENTER, TEXT_JUSTIFY_MIDDLE)
            BlzFrameSetLevel(button, 4)
            BlzFrameSetVisible(button, GetPlayerController(slots[GetLocalPlayer()][i]) == MAP_CONTROL_USER)

            local t = CreateTrigger()
            BlzTriggerRegisterFrameEvent(t, button, FRAMEEVENT_CONTROL_CLICK)
            TriggerAddAction(t, function ()
                print(GetPlayerName(slots[GetTriggerPlayer()][i]))
            end)

            TriggerRegisterPlayerEvent(leave, Player(i), EVENT_PLAYER_LEAVE)
            TriggerAddAction(leave, function ()
                if slots[GetLocalPlayer()][i] == GetTriggerPlayer() then
                    BlzFrameSetVisible(button, false)
                end
            end)
        end
    --end)

    local t = CreateTrigger()
    TriggerRegisterPlayerChatEvent(t, Player(0), "r ", false)
    TriggerAddAction(t, function ()
        local sl = tonumber(GetEventPlayerChatString():sub(3))
        RemovePlayer(Player(sl), PLAYER_GAME_RESULT_DEFEAT)
    end)
end)
Debug.endFile()
 
Last edited:
Level 24
Joined
Jun 26, 2020
Messages
1,853
Hello, I have an small issue with this, to create these frames I need to open and exit the alliance menu, but it makes it appear the menu for a milisecond, and I don't wanna that, I even added this to the script before the button is clicked:
Lua:
do
    local t = CreateTrigger()
    BlzTriggerRegisterFrameEvent(t, BlzGetFrameByName("UpperButtonBarAlliesButton", 0), FRAMEEVENT_CONTROL_CLICK)
    TriggerAddAction(t, function ()
        BlzFrameSetVisible(BlzGetFrameByName("AllianceDialog", 0), false)
        TriggerClearActions(t)
        DestroyTrigger(t)
    end)
end
But didn't work, what can I do?
 
Top