• 🏆 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!

Detect clicking of "Build Structure" button

Status
Not open for further replies.
Level 7
Joined
May 13, 2011
Messages
310
OK, I'm having real trouble with this. All I want to do is to be able to detect, either through an event or a condition, when any unit's build list is opened.

So, for example, let's say I have this:

  • Game - Display to (All players) the text: Choose a structure.
I want this to run when I click on the "Build Structure" button of one of my peasants. How do I do this?

Alternatively, if this isn't possible, I would like to know how to remove the "Build Structure" from a peasant's menu, while still being able to build (via triggers).


I don't mind using JASS, or even vJASS if necessary.
 
Level 37
Joined
Mar 6, 2006
Messages
9,240
  • Init
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Custom script: call TriggerRegisterGameEvent(gg_trg_Build_Menu, EVENT_GAME_BUILD_SUBMENU)
  • Build Menu
    • Events
    • Conditions
    • Actions
      • Game - Display to Player Group - Player 1 (Red) the text: Open
However I'm not sure if you can detect which player or unit clicked it.

EDIT: There's a GUI event for it also:
  • Game - The 'Build Structure' button is clicked
 
Level 7
Joined
May 13, 2011
Messages
310
Yeah, I saw that GUI event before, and I would use it in a single player game (as the WE says itself), but I'm really wondering if there is a method to detect the player that does this.

Still, thanks.
 
Level 7
Joined
May 13, 2011
Messages
310
create a trigger for each player with a local event and sync it somehow to avoid disconnects (you should use JASS for it, would be a mess with custom script)

How would I do this? That is, how would I "create a trigger with a local event"? I've never used local events before, or I haven't heard the term anyway.
EDIT: Would I use an if statement with GetLocalPlayer? If so, how do I put an event in an if statement?
 
Level 19
Joined
Feb 4, 2009
Messages
1,313
create a trigger called "Init", convert it to custom text and replace the script with this:
JASS:
function BuildActions takes nothing returns nothing
    //I don't use local variables here because I am not sure if they will create desyncs or somehow mess up the stack
    set udg_i = 0
    set udg_t = GetTriggeringTrigger()
    loop
        exitwhen udg_i>11
        if udg_t == udg_BuildTrigger[udg_i] then
            //This message will only be shown to Player(udg_i) anyway because it is local
            call DisplayTimedTextToPlayer(Player(udg_i), 0, 0, 60, GetPlayerName(Player(udg_i)) + " pressed the build-button")
            //This should (probably) be avoided because it creates an integer to diplay a message for all 12 players
            //call BJDebugMsg(GetPlayerName(Player(udg_i)) + " pressed the build-button")
            //Everything done below should be synced or not cause net traffic
            return
        endif
        set udg_i = udg_i + 1
    endloop
endfunction

function InitTrig_Init takes nothing returns nothing
    local integer i = 0
    loop
        exitwhen i>11
        set udg_BuildTrigger[i] = CreateTrigger()
        call TriggerAddAction(udg_BuildTrigger[i], function BuildActions)
        if GetLocalPlayer() == Player(i) then//all code below will be local and therefore is dangerous because it can cause desyncs
            call TriggerRegisterGameEvent(udg_BuildTrigger[i], EVENT_GAME_BUILD_SUBMENU)
        endif
        set i = i + 1
    endloop
endfunction
 
Level 7
Joined
May 13, 2011
Messages
310
images

That rocks. It works like a charm. Well, I haven't tried it in multiplayer yet, but it gives the player name which is exactly what should happen.

Thanks a million billion zillion.

EDIT: OK, now, you're still awesome, but I what I really need is to detect the unit of which the player clicked "Build Structure". Like, I only want my trigger (with the actual results) to affect the unit which is going to build. Understand?

Hope you can do this too :thumbs_up:
 
Last edited:
Level 18
Joined
Jan 21, 2006
Messages
2,552
Well when the Build Submenu is clicked the unit who is building is going to be a selected unit. This means that you only have to determine which units are currently being selected by the player.

There are also some dangerous actions performed in the above script:

JASS:
function BuildActions takes nothing returns nothing
    //I don't use local variables here because I am not sure if they will create desyncs or somehow mess up the stack
    set udg_i = 0
    set udg_t = GetTriggeringTrigger()
    loop
        exitwhen udg_i>11
        if udg_t == udg_BuildTrigger[udg_i] then
            //This message will only be shown to Player(udg_i) anyway because it is local
            call DisplayTimedTextToPlayer(Player(udg_i), 0, 0, 60, GetPlayerName(Player(udg_i)) + " pressed the build-button")
            //This should (probably) be avoided because it creates an integer to diplay a message for all 12 players
            //call BJDebugMsg(GetPlayerName(Player(udg_i)) + " pressed the build-button")
            //Everything done below should be synced or not cause net traffic
            return
        endif
        set udg_i = udg_i + 1
    endloop
endfunction

You should probably pre-initialize those strings so that the players' string tables don't prevent proper synchronization. Pretty simple:

JASS:
function BuildActions takes nothing returns nothing
    //I don't use local variables here because I am not sure if they will create desyncs or somehow mess up the stack
    set udg_i = 0
    set udg_t = GetTriggeringTrigger()
    loop
        exitwhen udg_i>11
        if udg_t == udg_BuildTrigger[udg_i] then
            //This message will only be shown to Player(udg_i) anyway because it is local
            call DisplayTimedTextToPlayer(Player(udg_i), 0, 0, 60, udg_BuildTriggerMessage[udg_i])
            //This should (probably) be avoided because it creates an integer to diplay a message for all 12 players
            //call BJDebugMsg(GetPlayerName(Player(udg_i)) + " pressed the build-button")
            //Everything done below should be synced or not cause net traffic
            return
        endif
        set udg_i = udg_i + 1
    endloop
endfunction

function InitTrig_Init takes nothing returns nothing
    local integer i = 0
    loop
        exitwhen i>11
        set udg_BuildTrigger[i] = CreateTrigger()
        set udg_BuildTriggerMessage[i] = GetPlayerName(Player(i)) + " pressed the \"Build\" button.")
        call TriggerAddAction(udg_BuildTrigger[i], function BuildActions)
        if GetLocalPlayer() == Player(i) then//all code below will be local and therefore is dangerous because it can cause desyncs
            call TriggerRegisterGameEvent(udg_BuildTrigger[i], EVENT_GAME_BUILD_SUBMENU)
        endif
        set i = i + 1
    endloop
endfunction

It also wasn't mentioned that you needed to have variables named "t" and "i", as well as a trigger array named "BuildTrigger" and (since my updates) a string array named "BuildTriggerMessage".

Anyways, as I was saying before in regards to your problems of detecting which unit actually clicked the button. In any situation where the "Build" button is clicked the unit must be selected. This means that enumerating the units selected by the player is going to give us a group in which one of the units "has clicked the build button".

If the group only contains a single unit, then it is narrowed down very easily. There is only a single unit who could have potentially clicked the "Build" menu. A condition could also be added to the group enumeration which disallows non-workers from being enumerated, which (in the case that many units are currently in selection) would further narrow the focus down.

The real problem is when multiple workers are in the player's selection. I suppose one could test to see how parallel the assignments of units in unit-groups are to the "grouped" unit selection. If they are identical then you could presume that the first unit in the unit-group is the one pressing the "Build" button. If they are not symmetric then you're going to have to guess which one is building (unless somebody has a crafty method for it).
 
Level 7
Joined
May 13, 2011
Messages
310
Wow, you really wrote a lot there. Thanks for doing all that, but I already figured out most of it.

I initialized those variables you said, specifically by using the GUI variable declarer, since I wanted to use them later on in GUI as well. Also, I figured out the whole "unit selected" thing. Fortunately my map only has one worker unit type per player, but I was just curious if there was a method for detecting the specific unit (for other maps, e.g. TD maps with multiple workers).

What makes the whole situation even simpler for me is that the effect that happens when the player clicks "Build Structure" is a Player effect, rather than a unit effect, so detecting the specific unit is not important. (So as you said, I can simply assume that its the first selected unit of that unit type.)

Still, thanks for all that info. People who find this thread in the future will surely find it useful. +rep

EDIT: Well, I can't believe it, but I've come upon yet another little problem. It's the last one, I promise.

When I click the "Build Structure" button, the submenu of structures opens, right? Well, what I want to do is to "delay" this opening-of-the-submenu either:

  • For a certain amount of time (like, half a second) or
  • Until I open it again with a "Order Unit to Build" trigger

If this isn't possible then it isn't a tragedy, my stuff still works, but it would be desirable. And please don't suggest forcing the player to press Esc/Cancel, I tried that and you can see the menu for half a second, I want it to not open at all.
 
Last edited:
Level 7
Joined
May 13, 2011
Messages
310
Bump... (is this allowed?)

See the edit on my previous post, I want to know if it's possible. As I said it doesn't absolutely ruin my stuff, but it would be really, really helpful.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
When I click the "Build Structure" button, the submenu of structures opens, right? Well, what I want to do is to "delay" this opening-of-the-submenu either:

Well there isn't really any secret way of doing this, at least that I know of. Delaying UI commands is completely out of the Warcraft III API. I suppose if you wanted to indirectly achieve the effect there are a few things you could try.

And please don't suggest forcing the player to press Esc/Cancel, I tried that and you can see the menu for half a second, I want it to not open at all.

Have you tried pausing the unit (instead of pressing ESC)? This may cause the UI to react instantly and prevent the available structures from being displayed. Then after a brief period you could force the UI-key associated with Build Structure (typically it's B).
 
Level 7
Joined
May 13, 2011
Messages
310
When you say pause, do you mean order them to "Stop"? I could try that. The only problem would be that if you click "Build Structure" while the unit is moving, he'll stop in place, which would be quite a big bug.
Still, I'll try it out and see if it even works at all. I'll edit this post with the results later.

EDIT: Didn't work, unfortunately. I tried putting the command of forcing a UI Cancel as early as possible, just when the player clicks it, but it still displayed for a fraction of a second.

I guess I'll just have to put up with it :ugly:
 
Last edited:
Level 7
Joined
May 13, 2011
Messages
310
Tried that, still doesn't work. I think the problem is the reaction time. Even if I put Pause/Stop/whatever as early as possible, it takes a noticeable fraction of a second to occur.

Unfortunately there doesn't seem to be a remedy for this. I will either have to put up with it, or find a method to remove the Build Structure button from the UI (but still make the Build order triggerable).

If you would know a method for that, it'd help a great deal.
 
Level 7
Joined
May 13, 2011
Messages
310
you could use a morph spell to replace the worker with a different one or replace the worker with triggers
but that would cancel all orders
you could catch the first order with some system and apply it after swapping though

I knew someone would suggest this, that's why I asked questions about the Build Structure buttons and not about multiple build menus.
I was even more afraid that someone would link me here. I've seen this, downloaded the map, looked over how it's done, but it just isn't what I want. This method requires the user to click on the "Bear Form" spell whenever they want to change build lists. I can understand that for some maps this would be great, but unfortunately it just doesn't cut it for mine.
I also understand that I could link one of the morphing spells with the Build Structure button, but that would still give me the problem of it only happening after a fraction of a second. I'm not gonna bother with 2 units if I don't have to.

All I'm asking right now is a way to remove Build Structure from the UI (and replace it with my own ability).
 
Level 23
Joined
Jan 1, 2009
Messages
1,609
You can just replace the structures build with abilities, the problem is that you wont get any preview/pathing check implemented.
You can use the tiny buildings ability, but they all got the same ID, meaning you can only use 1 on 1 commandcard.

I tested one way in my Map escape builder, using chaos/bear form etc to simulate 4 menus, so that when you used bear form the unit got ordered to "build", opening the build menu and then forcing the UI key to morph back when closing the build menu.

The problem is that the order "build" for 1 unit vanishes the command cards for all other selected units. Blizzfail hooray!
 
Level 7
Joined
May 13, 2011
Messages
310
Well, thanks for trying to help. But as I said, I discovered the morph method, and also things like the "build tiny technique".

My technique is actually pretty much perfected, the only thing is the delay of an effect after I click Build Structure (see previous page for details). The only remedy for this I can think of for this is completely removing the Build Structure button from the UI and replacing it with my own ability.

However I have a question that might help me: how did you detect when the user closes the build menu?
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
That's even harder. I've been trying to detect exactly when a player is targeting with a unit (selecting a target for a spell) so that during this period I could do things, but I can't find a good way to detect when the targeting process stops (which is similar to the Build Structure being closed). Tides of Blood did it well in vO but they never share an ounce of their knowledge with the rest of the community.
 
Level 7
Joined
May 13, 2011
Messages
310
Don't give the unit any structures to be built?

I probably did not get what you wanted to say
Why don't you tell us what you want to do?

Because then you will (probably) come up with more suggestions like morphing and build-tiny which I have come along countless times.
Still, as long as nobody starts doing that (which I can't really count on), I guess it might help to say what I'm doing.

What I want is multiple build menus, Basic Build and Advanced Build. Right now I am testing with a test map, where the basic structures are Human structures and the advanced structures are Orc structures.
The method I am using for this is that when I click on Basic Build (which is the Build Structure button) it makes the Human structures available and the Orc ones unavailable. The Advanced Build button makes Orc buildings available and human ones unavailable.

However, the thing is that with the Advanced Build button is a custom ability that, when clicked, triggers the availability/unavailability of structures, and then opens the build menu. Unfortunately, since Basic Build is actually Build Structure, it will open the build menu as soon as I click on it, which gives me no time to change the structure availability before the menu opens. So, if the Advanced Build menu has been opened, when I click Basic Build it shows the menu a moment before the trigger comes into effect, which results in people seeing the Orc buildings for a moment before the Human building return. Understand?

I've tried multiple things, but I have come to the point that the only solution I can think of is either removing the Build Structure button and replacing it with my own ability (so that it's like Advanced Structures), or detect when the player clicks cancel (so that I can make the availability changes once the player closes the Advanced Build menu).

All I want right now is for one of those to be possible, somehow.

Tides of Blood did it well in vO but they never share an ounce of their knowledge with the rest of the community.

That sucks.
 
Status
Not open for further replies.
Top