• 🏆 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!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Lua] Experimental fix snippet for FRAMEEVENT_ENTER/LEAVE bug (v1.33+)

Level 14
Joined
Feb 7, 2020
Messages
387
ded gaem

Can't figure out a more elegant solution. Guess it's FDFs or bust.

I was experimenting with some methods to make a fix snippet for the ENTER/EXIT infinite loop bug introduced by Deforged 1.33.

The demo map is attached.

With some caffeine and nerd rage, I ended up with something that semi-works (this is a functional test, so local player isn't implemented).

It registers leave functions independent of the frame event API and instead runs them when a 'listener frame' receives the next ENTER event (since only one frame can be in context at a time). Not too complicated of an idea. This is accomplished alongside some flag management madness (toggling an "outside of button" boolean and then setting the most recent mouse enter framehandle).

However, it's not perfect. The primary issue is that if you mouse over a button very quickly before the next 'enter' event can register then only the 'enter' function runs - this can be solved by widening the size of the catch frame or by using a single fullscreen listener, but that would then limit how buttons can be used around the screen.

I tried a few twisted contortions, this has been the closest solution so far.

Is this over-engineered? Has anyone had success with their own experimental fixes?

Lua:
--- An experimental fix for the WC3 Reforged v1.33 mouse enter/exit infinite loop bug.
--- By Planetary @ hiveworkshop.com

----------------------------------
-- REQUIRED GLOBALS AND FUNCS   --
----------------------------------

LAST_TRIGGER_FRAMEHANDLE = {
    [FRAMEEVENT_MOUSE_ENTER] = 0,
    [FRAMEEVENT_MOUSE_LEAVE] = 0,
    -- UP/DOWN events no longer work as of 1.33, use CONTROL_CLICK instead
}
TRIGGER_MOUSE_EXIT_FUNCTION = {}
TRIGGER_MOUSE_EXIT_IS_OUTSIDE = false
TRIGGER_CATCH_EXIT_FH = 0

function TriggerMoveExitCatchFrame(to_target_fh)
    BlzFrameClearAllPoints(TRIGGER_CATCH_EXIT_FH)
    BlzFrameSetPoint(TRIGGER_CATCH_EXIT_FH, FRAMEPOINT_CENTER, to_target_fh, FRAMEPOINT_CENTER, 0, 0)
    BlzFrameSetSize(TRIGGER_CATCH_EXIT_FH, BlzFrameGetWidth(to_target_fh)*1.3, BlzFrameGetHeight(to_target_fh)*1.6)
end

function TriggerShouldRunMouseEvent(fh, frameevent)
    if frameevent == FRAMEEVENT_MOUSE_ENTER then
        if LAST_TRIGGER_FRAMEHANDLE[FRAMEEVENT_MOUSE_ENTER] and LAST_TRIGGER_FRAMEHANDLE[FRAMEEVENT_MOUSE_ENTER] == fh then
            return false
        else
            LAST_TRIGGER_FRAMEHANDLE[FRAMEEVENT_MOUSE_ENTER] = fh
            TRIGGER_MOUSE_EXIT_IS_OUTSIDE = false
            TriggerMoveExitCatchFrame(fh)
            return true
        end
    end
end

-- helper function, not needed in normal use:
function TriggerAddMouseAction(t, fh, eventtype, func)
    TriggerAddAction(t, function()
        if BlzGetTriggerFrame() == fh and TriggerShouldRunMouseEvent(fh, eventtype) then
            func()
        end
    end)
end

-- new API function:
function TriggerAddMouseEnterAction(t, fh, func)
    TriggerAddMouseAction(t, fh, FRAMEEVENT_MOUSE_ENTER, func)
end

-- new API function:
function TriggerAddMouseLeaveAction(t, fh, func)
    -- support multiple actions:
    if not TRIGGER_MOUSE_EXIT_FUNCTION[fh] then
        TRIGGER_MOUSE_EXIT_FUNCTION[fh] = {}
    end
     table.insert(TRIGGER_MOUSE_EXIT_FUNCTION[fh], function()
        func()
    end)
end

----------------------------------
-- DEMO                         --
----------------------------------

do
    local real = MarkGameStarted
    function MarkGameStarted()
        real()

        local enter_t, exit_t = CreateTrigger(), CreateTrigger()
        local catch_exit_fh = BlzCreateFrameByType("BUTTON", "MyCatchExitButton", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), "", 0)
        local button = BlzCreateFrameByType("GLUETEXTBUTTON", "MyScriptDialogButton", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), "ScriptDialogButton", 0)
        local button2 = BlzCreateFrameByType("GLUETEXTBUTTON", "MyScriptDialogButton2", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), "ScriptDialogButton", 0)

        BlzFrameSetTexture(catch_exit_fh, "UI\\Widgets\\EscMenu\\Human\\blank-background.blp", 0, true)
        BlzFrameSetAlpha(catch_exit_fh, 0)

        BlzTriggerRegisterFrameEvent(enter_t, catch_exit_fh, FRAMEEVENT_MOUSE_ENTER)
        BlzTriggerRegisterFrameEvent(enter_t, button, FRAMEEVENT_MOUSE_ENTER)
        BlzTriggerRegisterFrameEvent(exit_t, button, FRAMEEVENT_MOUSE_LEAVE)
        BlzTriggerRegisterFrameEvent(enter_t, button2, FRAMEEVENT_MOUSE_ENTER)
        BlzTriggerRegisterFrameEvent(exit_t, button2, FRAMEEVENT_MOUSE_LEAVE)

        BlzFrameSetSize(catch_exit_fh, 0.25, 0.3)

        BlzFrameSetAbsPoint(catch_exit_fh, FRAMEPOINT_CENTER, 0.4, 0.35)
        BlzFrameSetAbsPoint(button, FRAMEPOINT_CENTER, 0.4, 0.4)
        BlzFrameSetAbsPoint(button2, FRAMEPOINT_CENTER, 0.4, 0.3)

        -- set the Button's text
        BlzFrameSetText(button, "Example Hover Button 1")
        BlzFrameSetText(button2, "Example Hover Button 2")

        TriggerAddAction(enter_t, function()
            if BlzGetTriggerFrame() == TRIGGER_CATCH_EXIT_FH and LAST_TRIGGER_FRAMEHANDLE[FRAMEEVENT_MOUSE_ENTER] then
                if not TRIGGER_MOUSE_EXIT_IS_OUTSIDE and TRIGGER_MOUSE_EXIT_FUNCTION[LAST_TRIGGER_FRAMEHANDLE[FRAMEEVENT_MOUSE_ENTER]] then
                    for _,func in ipairs(TRIGGER_MOUSE_EXIT_FUNCTION[LAST_TRIGGER_FRAMEHANDLE[FRAMEEVENT_MOUSE_ENTER]]) do
                        func()
                    end
                    TRIGGER_MOUSE_EXIT_IS_OUTSIDE = true
                    LAST_TRIGGER_FRAMEHANDLE[FRAMEEVENT_MOUSE_ENTER] = nil
                end
            end
        end)

        -- demo 1-time run functions (no longer looping infinitely):
        TriggerAddMouseEnterAction(enter_t, button, function()
            print("|cffffff00button 1 ENTER function|r")
        end)
        TriggerAddMouseEnterAction(enter_t, button2, function()
            print("|cff00ff00button 2 ENTER function|r\n")
        end)
        TriggerAddMouseLeaveAction(exit_t, button, function()
            print("|cffffff00button 1 EXIT function|r")
        end)
        TriggerAddMouseLeaveAction(exit_t, button2, function()
            print("|cff00ff00button 2 EXIT function|r\n")
        end)

        TRIGGER_CATCH_EXIT_FH = catch_exit_fh
    end
end
 

Attachments

  • z_interface_sandbox.w3m
    97.9 KB · Views: 9
Last edited:
Level 6
Joined
Jan 12, 2011
Messages
110
Anyone has any feedback on this?
Been plaguing our map and I guess w8ing for resolution means we die waiting for it to be fixed.
My only other possible solution would be to use (if possible) some diff types of buttons that do not glitch - so far it seems SIMPLE buttons do not trigger this issue...
 
Level 18
Joined
Oct 17, 2012
Messages
820
There are a few options:
  1. You can use SIMPLEBUTTON. To give the button a custom texture in game, import a fdf file with the following contents:
    JASS:
    Frame "SIMPLEBUTTON" "ExampleButton" {
        Width 0.035,
        Height 0.035,
        Texture "ExampleButtonTexture" {
        }
    }
    • The frame can leave the 4:3 area.
  2. POPUPMENU
  3. Wrap SIMPLEFRAME around the needed frame type.
    JASS:
    Frame "SIMPLEFRAME" "TestButtonSimpleFrame"  { 
       Frame "BUTTON" "TestButton" { 
       }
    }
    • The frame cannot leave the 4:3 area.
 
Top