Retera / WarsmashModEngine

An emulation engine to improve Warcraft III modding
GNU Affero General Public License v3.0
192 stars 37 forks source link

Prevent units from being highlighted behind UI by mouse. #53

Closed bearU369 closed 3 months ago

bearU369 commented 7 months ago

This is a PR attempting to stop units behind UI from being highlighted/selected. It's partially able to stop highlighting units behind frames in consoleUI but timeIndicator frame and some dialogs (Game Menu, Victory, Exit, etc.) were able to highlight units. It is wonky right now so I would be aiming to do an elegant implementation of this.

Retera commented 7 months ago

Out of curiosity, if you use the existing getFrameChildUnderMouse(...) != null instead of mouseOverUI, does it fail for some reason? I guess that existing system only reported a value for things that were meant to be clickable?

If that's the case, what's the rationale for not making mouseOverUI or whatever we want to call it a recursive thing that is likewise processed by child frames? It still might not fix the time indicator since that one is a wonky 3D model sprite frame, but it would probably fix the escape menu if I'm recalling things correctly.

bearU369 commented 7 months ago

Out of curiosity, if you use the existing getFrameChildUnderMouse(...) != null instead of mouseOverUI, does it fail for some reason? I guess that existing system only reported a value for things that were meant to be clickable?

Yeah, getFrameChildUnderMouse only picks up the clickable frames as it failed to find the decorated frames since they only recursively jumps to other frames. Initially tried to implement them being picked up as well but something like button highlights starting to break in the main menu.

If that's the case, what's the rationale for not making mouseOverUI or whatever we want to call it a recursive thing that is likewise processed by child frames?

If I understand, the parent frame is already stretched out by its child frames so its not really worth continuing to process recursively. I noticed from consoleUI since it has child frames from upper buttons to inventory frame, encompassing most of the screen as I tried to debug why it failed to pick up units outside of the UI beforehand. I'm still trying to make the function able to recursively check other frames so I would only pick up the rootFrame but I'm having difficulty trying to make the function differentiate between a decorated frame and a blank, parent frame.

I wonder if there's a way to intersect the model with a mouse for to able to be picked up instead of the renderBounds if that's the case. It seems to work for picking up units in war3MapViewer but I'm not sure how it can handle picking up 3D UI model.

bearU369 commented 7 months ago

After some tweaks and bugfixing, it's stable enough for gameplay thus ready to be merged. Although this have not yet resolve the issue with SpriteFrame

Retera commented 7 months ago

I did some brainstorming looking at this PR tonight. Some ideas:

So I kind of went in a circle -- thinking that maybe I would recommend this be some enum with minimal code aiming for high performance -- back to thinking maybe you had it right and we simply didn't understand the rule for which frames block out the mouse and which don't. And that led me to some intuitions about what I think would help:

"EscMenuMainPanel" (not a Warsmash file, provided by game assets in UI\FrameDef\UI\EscMenuMainPanel.fdf) is using SetAllPoints meaning that this frame expands to fill its container. Warsmash is abusing the fact that its windowing system is hacked together and allows the parent of a frame not to necessarily entirely contain its child frame, and thus wraps "EscMenuMainPanel" inside of an invented container "SmashEscMenu" (software defined) that has zero size but is used for some hacked offset from the top of the screen to try to make the sizing and position on this stuff look loosely like WC3. So although "EscMenuMainPanel" on Warsmash is expanding to fill the size of its container, its container "SmashEscMenu" is probably a zero-side degenerate single point of a frame if I was reading the code correctly. By contrast, with how this probably should have been, I'm theorizing that "EscMenuMainPanel" on Warcraft 3 probably is expanding to fill the entire actual screen, thus preventing use of the mouse for 3D game world interactions while browsing the menus. Doing that would honestly make a lot of sense. Similarly, I never found the files that defined "TimeOfDayIndicator" SpriteFrame on Warcraft 3 and so in Warsmash I am using an invented one <WarsmashModEngine>/resources/UI/FrameDef/SmashUI/TimeOfDayIndicator.fdf and this is probably also aberrantly spawning with width=0 and height=0 because there is not currently a constraint that the 3D sprite of a sprite frame must fall within the bounding of its corresponding UIFrame.

In the long term, this makes me think it might be better to:

  1. Completely delete "SmashEscMenu"
  2. Spawn "EscMenuMainPanel" as a child of rootFrame (the GameUI) so that it fills the whole screen
  3. Allow the bounding of all frames to prevent the mouse from intersecting the 3D game world (technically on the Blizzard engine, the 3D game world is itself a frame called WorldFrame but Warsmash is more of spaghetti code than that)
  4. Provide a width and height for <WarsmashModEngine>/resources/UI/FrameDef/SmashUI/TimeOfDayIndicator.fdf so that it also prevents clicking the 3D game world, because using the same UIFrame click interaction is probably easier and more performant than doing a 3D mesh intersect (although 3D mesh intersect would be physically possible if desired, like you mentioned, but I just think it's probably currently totally unnecessary)
  5. Perhaps then we need only special case "SmashHpBar" and "SmashHoverTip" stuff to avoid blocking the mouse [Although on Blizzard's more robust system maybe these are children of WorldFrame and thus not even special cased because WorldFrame takes ownership of the mouse for that ?? Or maybe not ]

But despite all of these ideas described above, I definitely appreciate that the current UI system is a mess because I tried to make my own thing in order to be able to stylize the FDF loading in the way that I wanted, and it's very hacked together in places currently. So I'll be tempted to merge this anyway after getting your thoughts.

bearU369 commented 7 months ago

A tagging system for UIFrames would've made a lot sense for frame with varieties like the SmashHpBar variables though I'm not sure if the system will be only used in MeleeUI or could be used elsewhere too, like an rework with the UIFrame's getFrameChildUnderMouse that would recognize backdrop frames too instead of having a two, identical methods aiming to do the same?

Though for now, getHoveredFrame isn't playing nice with finding the current UIFrame accurately, as SmashConsoleInventoryCover has renderbound bigger than what it's texture rendered and UnitPortrait somehow when hovering the mouse near the bottom of the screen. Fortunately its main purpose for now is to blocking the mouse from interacting with the ingame while in the UI so it's not a big deal as for now but could be an issue if we intend to merge both functions.

I believe that during multiplayer, the menu wouldn't block out the screen as everything still functions ingame (unless one of the player asked for a pause) compared in the single-player, the menu will block the whole screen as everything is on pause. So I think Wc3's UI system works pretty robust with its mouse interaction than having a different states for UI interactions as the EscMainMenuPanel covering the whole screen is probably a cheesy way of preventing any ingame interaction with the mouse during a pause on singleplayer.