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

[Solved] Build order causing all player builders to open build menu

Status
Not open for further replies.
Level 5
Joined
Jan 23, 2020
Messages
86
I want to trigger a worker unit to automatically change to its build mode when an ability is used.

Issue: Triggering this on the player's unit causes all players worker units to open their build mode, I just want the triggering unit to do this.

  • Build Trigger
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Test Build Ability
    • Actions
      • Unit - Order (Triggering unit) to Build.

Actual trigger if context helps you Original trig thread
 
Level 39
Joined
Feb 27, 2007
Messages
4,994
That's... very weird. Never have I seen an issue with Starts the effect of an ability or issuing an order like this. Perhaps you can narrow down the issue by doing something else to the unit instead. Kill it, move it, or set its vertex color. Does that affect all builder units or is it just the order that gets duplicated?
 
Level 5
Joined
Jan 23, 2020
Messages
86
That's... very weird. Never have I seen an issue with Starts the effect of an ability or issuing an order like this. Perhaps you can narrow down the issue by doing something else to the unit instead. Kill it, move it, or set its vertex color. Does that affect all builder units or is it just the order that gets duplicated?

It's directly related to the Build action, moving or stopping only affects the triggering unit. Is there an alternative way to access that build option without using this action? (besides dummies or spell books), or better yet, not having to make the player esc/cancel the "dummy" structure that triggers this.
 
Last edited:
Level 5
Joined
Jan 23, 2020
Messages
86
Sounds like you need to limit the action to only execute for the unit owner. Custom trigger code with GetOwningPlayer(unit) and GetLocalPlayer(). Not tested:
JASS:
if GetOwningPlayer( GetTriggerUnit() ) == GetLocalPlayer() then
// issue Build
endif

I think this is working, but it causes a desync for the other player. Any way to prevent this?

  • Test
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Test Ability
    • Actions
      • Custom script: if GetOwningPlayer( GetTriggerUnit() ) == GetLocalPlayer() then
      • Unit - Order (Triggering unit) to Build.
      • Custom script: endif
 
Level 19
Joined
Jan 3, 2022
Messages
320
OK I figured out why it can't work. I used ingame console:
Lua:
trg_gameev = CreateTrigger()
TriggerAddAction(trg_gameev, function() print(GetTriggerEventId()) end)
TriggerRegisterGameEvent(trg_gameev, EVENT_GAME_BUILD_SUBMENU)
When you open the build menu, a game event EVENT_GAME_BUILD_SUBMENU is fired. New event = new handle = desync. ForceUICancel should be fine locally, async. Would you try this?
JASS:
// issue Build
if GetOwningPlayer( GetTriggerUnit() ) != GetLocalPlayer() then
    // For any player who isn't the target, force cancel menu
    call ForceUICancel()
endif
This doesn't work, idk at this point.
What I don't know what will happen if another player has a menu open that's not a build menu. Imagine:
  1. Player A opens spell book menu
  2. Player B's unit casts special ability, build menu is opened for him. Though ForceUICancel is send to all other players
  3. Will Player A's unit's spell book menu remain open?
Please test this. I will try to find another way without ForceUICancel by using BlzUnitDisableAbility though it's buggy.
 
Last edited:
Level 5
Joined
Jan 23, 2020
Messages
86
Is this the correct way you wanted the JASS to be entered?

  • Test
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Test
    • Actions
      • Unit - Order (Triggering unit) to Build.
      • Custom script: if GetOwningPlayer( GetTriggerUnit() ) != GetLocalPlayer() then
      • Custom script: call ForceUICancel()
      • Custom script: endif
Still causes desync for the other player (testing on 2 instances of war3 on my PC with LAN)

EDIT: Yea its a tricky one, I'll probably just resort to using the Bear Form method for this
 
Level 19
Joined
Jan 3, 2022
Messages
320
Well here's a proof of concept should work, but there're problems with BlzUnitDisableAbility. It's the best I've got.
  1. Disable all other players' workers' build menu with BlzUnitDisableAbility (I've got the ability IDs from here.)
    1. "AHbu" (Build (Human)), "AObu" (Build (Orc)), "AEbu" (Build (Night Elf)), "AUbu" (Build (Undead))
    2. BlzUnitDisableAbility(otherUnits, <raw code for build>, true, false)
  2. Open build menu for current unit (the only unit with enabled build menu)
    1. IssueImmediateOrder(targetUnit, "humanbuild")
  3. Reenable others build menus
    1. BlzUnitDisableAbility(otherUnits, <raw code for build>, false, false)
If your trigger is the only one that will use BlzUnitDisableAbility() then it's ok. There is some bug where if you make it hide/disable an ability 2+ times, you'll need an unknown amount of "show" calls to show it again.
PS: The idea is to disable the build menu/unit so it doesn't get opened. I'll see if there are other methods
Issue: Triggering this on the player's unit causes all players worker units to open their build mode, I just want the triggering unit to do this.

Can you explain in detail what happens? If player 1 and 2 have workers selected, did the build menu open for both? What if workers are selected as part of a group?
 
Last edited:
Level 5
Joined
Jan 23, 2020
Messages
86
I don't fully understand custom scripts so I'll need some help on that.

This is what I understood from your post:
  • Test
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Test Ability
    • Actions
      • Custom script: BlzUnitDisableAbility(otherUnits, "AUbu" (Build (Undead)), true, false)
      • Custom script: IssueImmediateOrder(targetUnit, "undeadbuild")
      • Custom script: BlzUnitDisableAbility(otherUnits, "AUbu" (Build (Undead)), false, false)
Can you explain in detail what happens? If player 1 and 2 have workers selected, did the build menu open for both? What if workers are selected as part of a group?
If player 1 accesses the build menu and presses the "Dummy" building named "Next page" it will trigger the current available buildings on the builder to be unavailable and a new set of building options to become available. It then disabled "Next page" structure dummy and enables "Previous page" dummy unit for an option to revert back. Essentially paging between buildings.

After this I force esc/cancel the player so that the "Next page" dummy looses build selection, then I bring back the build order so the player just sees the next page. That's where the issue occurs

  • Next Page
    • Events
      • Game - Button for build Building Next pressed.
    • Conditions
    • Actions
      • Set VariableSet PN = (Player number of (Owner of (Triggering unit)))
      • -------- --------
      • Player - Make Shredder Unavailable for training/construction by (Player(PN))
      • Player - Make Splash Unavailable for training/construction by (Player(PN))
      • Player - Make Multi Unavailable for training/construction by (Player(PN))
      • Player - Make Death Tower Unavailable for training/construction by (Player(PN))
      • Player - Make Fighter Unavailable for training/construction by (Player(PN))
      • Player - Make AA Unavailable for training/construction by (Player(PN))
      • Player - Make Moon Unavailable for training/construction by (Player(PN))
      • Player - Make Magic Unavailable for training/construction by (Player(PN))
      • Player - Make Lazer Unavailable for training/construction by (Player(PN))
      • Player - Make Rapid Unavailable for training/construction by (Player(PN))
      • Player - Make Sharp Unavailable for training/construction by (Player(PN))
      • -------- --------
      • Player - Make Building Next Unavailable for training/construction by (Player(PN))
      • Player - Make Building Previous Available for training/construction by (Player(PN))
      • Player - Make Mystic Available for training/construction by (Player(PN))
      • Player - Make Nether Available for training/construction by (Player(PN))
      • Player - Make Guts Available for training/construction by (Player(PN))
      • Player - Make Evoker Available for training/construction by (Player(PN))
      • Player - Make Moon Available for training/construction by (Player(PN))
      • Player - Make Beast Available for training/construction by (Player(PN))
      • -------- --------
      • Game - Force (Owner of (Triggering unit)) to press Escape/Cancel
      • -------- --------
      • Unit - Order (Triggering unit) to Undead Build.
  • Prev Page
    • Events
      • Game - Button for build Building Previous pressed.
    • Conditions
    • Actions
      • Set VariableSet PN = (Player number of (Owner of (Triggering unit)))
      • -------- --------
      • Player - Make Shredder Available for training/construction by (Player(PN))
      • Player - Make Splash Available for training/construction by (Player(PN))
      • Player - Make Multi Available for training/construction by (Player(PN))
      • Player - Make Death Tower Available for training/construction by (Player(PN))
      • Player - Make Fighter Available for training/construction by (Player(PN))
      • Player - Make AA Available for training/construction by (Player(PN))
      • Player - Make Moon Available for training/construction by (Player(PN))
      • Player - Make Magic Available for training/construction by (Player(PN))
      • Player - Make Lazer Available for training/construction by (Player(PN))
      • Player - Make Rapid Available for training/construction by (Player(PN))
      • Player - Make Sharp Available for training/construction by (Player(PN))
      • -------- --------
      • Player - Make Building Previous Unavailable for training/construction by (Player(PN))
      • Player - Make Building Next Available for training/construction by (Player(PN))
      • Player - Make Mystic Unavailable for training/construction by (Player(PN))
      • Player - Make Nether Unavailable for training/construction by (Player(PN))
      • Player - Make Guts Unavailable for training/construction by (Player(PN))
      • Player - Make Evoker Unavailable for training/construction by (Player(PN))
      • Player - Make Moon Unavailable for training/construction by (Player(PN))
      • Player - Make Beast Unavailable for training/construction by (Player(PN))
      • -------- --------
      • Game - Force (Owner of (Triggering unit)) to press Escape/Cancel
      • -------- --------
      • Unit - Order (Triggering unit) to Build.

ALL players in the game will have their build menu open (if they have thier builder selected) if a regular unit is selected it will just cancel/deselect that unit (leaving a blank unit GUI example) as it does not have a build option.

Original trig thread
 
Last edited:
Level 39
Joined
Feb 27, 2007
Messages
4,994
Well here's a proof of concept should work
I agree that this should work, but it might have an unintended side-effect. Instantaneously disabling and then re-enabling the build ability will probably fuck with any player who currently has the build menu open. So if Player 2 has build menu open and while that's the case Player 1 clicks the Next button to page forward it will probably close the build menu for Player 2. Maybe this is acceptable but in a worst-case scenario it could be abused by repeatedly switching pages to prevent other players from being able to build if they can't do it quickly enough.

I don't fully understand custom scripts so I'll need some help on that.
Is your map set to use Lua or JASS? I honestly don't even know what the default is anymore, and that affects the need for keywords like set/call. AFAIK you can use the GUI disable/enable actions just fine if you save the build ability into an ability code variable (actually just an integer but GUI classifies them differently because fuck simplicity) and then refer to that instead of the ability directly (since those abilities cannot be directly referred to in GUI).
  • -------- do this on map init --------
  • -------- in this example the int variable is called BUILD_ABILITY_ID --------
  • -------- the first line is for JASS maps, the second is for Lua maps, you do not need both --------
  • Custom script: set udg_BUILD_ABILITY_ID = 'AUbu'
  • Custom script: udg_BUILD_ABILITY_ID = FourCC("AUbu")
  • Game - Force (Owner of (Triggering unit)) to press Escape/Cancel
  • Set BuildGroup = (Units in (Playable Map Area) matching ((Unit-type of (Matching Unit)) equal to YOUR BUILDER UNIT TYPE))
  • Unit Group - Remove (Triggering Unit) from BuildGroup
  • Unit Group - Pick every unit in BuildGroup and do (Actions)
    • Loop - Actions
      • Unit - For (Picked unit), Ability BUILD_ABILITY_ID, Disable ability: True, Hide UI: False
  • Unit - Order (Triggering unit) to Undead Build.
  • Unit Group - Pick every unit in BuildGroup and do (Actions)
    • Loop - Actions
      • Unit - For (Picked unit), Ability BUILD_ABILITY_ID, Disable ability: False, Hide UI: False
  • Custom script: call DestroyGroup(udg_BuildGroup) //remove the "call" if your map is Lua, also make this match you group variable name
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,517
Is your map set to use Lua or JASS? I honestly don't even know what the default is anymore
The World Editor still defaults to Jass.

I'd say that the majority of people on this forum are still using GUI + Jass. I can't speak for everyone but the Lua users seem to be the types that weren't using GUI even before Lua was released. Plus with languages like C# and TypeScript that can be used and compiled to Lua there's way more options for coding. I'd like more GUI users to start making maps in Lua mode, it'd be a massive improvement, but a lot of them are unaware of it's existence and the state of Warcraft 3 ain't really helping.

@Droof
Custom scripts are just Jass code (unless on Lua mode, then they're Lua code).

Do you know how you can convert a GUI trigger to code? Basically any line of code that you would see in that converted trigger could be written in Custom script to do the same thing.

These Actions are the exact same thing it's just that the first line uses GUI (Graphical User Interface) and the other uses the actual code form of this action.
So both of these are technically Jass code:
  • Unit - Kill (Triggering unit)
  • Custom script: call KillUnit(GetTriggerUnit())
 
Last edited:
Level 5
Joined
Jan 23, 2020
Messages
86
@Pyrogasm Does not seem to be working (Player 2 has their build option disabled, but it still opens the menu)

Variable:
Capture.JPG
Variable Setup:
  • Setup Build ability
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Custom script: set udg_BUILD_ABILITY_ID = 'AUbu'
Unit Trigger (with debug):
  • Next Page
    • Events
      • Game - Button for build Building Next pressed.
    • Conditions
    • Actions
      • -------- Add/Remove units above --------
      • -------- --------
      • Game - Force (Owner of (Triggering unit)) to press Escape/Cancel
      • -------- --------
      • Set VariableSet BuildGroup = (Units in (Playable map area) matching ((Unit-type of (Matching unit)) Equal to BuilderType))
      • Unit Group - Pick every unit in (Units of type BuilderType) and do (Actions)
        • Loop - Actions
          • Game - Display to (All players) the text: (Name of (Picked unit))
      • Unit Group - Remove (Triggering unit) from BuildGroup.
      • Game - Display to (All players) the text: (Remove this unit from group + (Name of (Triggering unit)))
      • Unit Group - Pick every unit in BuildGroup and do (Actions)
        • Loop - Actions
          • Unit - For (Picked unit), Ability BUILD_ABILITY_ID, Disable ability: True, Hide UI: False
          • Game - Display to (All players) the text: (Name of disabled build ability: + (Name of BUILD_ABILITY_ID))
      • Unit - Order (Triggering unit) to Undead Build.
      • Game - Display to (All players) the text: (Order + ((Name of (Triggering unit)) + to build))
      • Unit Group - Pick every unit in BuildGroup and do (Actions)
        • Loop - Actions
          • Unit - For (Picked unit), Ability (Ability being cast), Disable ability: False, Hide UI: False
          • Game - Display to (All players) the text: (Name of enabled build ability: + (Name of BUILD_ABILITY_ID))
      • Custom script: call DestroyGroup(udg_BuildGroup)
Debug Messages in-game:
Capture4.JPG
Disabled build menu for player 2:
Capture3.JPG

Other actions I have tried for player 2:
Stop, Pause/Un-pause, Removing/Adding peon-type classification, Removing/Adding Build Ability (causes blank UI)

Even if I remove player 2's builder from the game, and create it AFTER player 1 has triggered build, it will STILL open player 2's build menu...
 
Last edited:
Level 39
Joined
Feb 27, 2007
Messages
4,994
There's an error in this line where you used Ability Being Cast: Unit - For (Picked unit), Ability (Ability being cast), Disable ability: False, Hide UI: False but I don't think that is the issue here.

Even if I remove player 2's builder from the game, and create it AFTER player 1 has triggered build, it will STILL open player 2's build menu...
This is so fucking weird. I thought to myself "there's no way shit like this can be happening" so I built my own test map and here's what I found (current patch is 1.32.10.18067):
  • It happens for all worker units no matter what build order is actually issued. For example you could use humanbuild, nagabuild, or orcbuild and it will still make an acolytes and wisps enter their build menus.
  • Other non-targeted orders are not duplicated - I tested with Berserk and the other workers did not cast it, only the proper unit.
  • The use of Triggering Unit is not causing some sort of issue here; I replaced it with a global variable and the build menu open was still duplicated.
  • Issuing the build order to a null unit variable, a dead unit, a paused unit, or a unit that cannot build anything does not produce duplication. Presumably other instances of the unit not being able to build (Banish, in Cargo Hold, Stunned, etc.) would also not produce duplication, but I did not check these.
  • It's not an issue with the specific worker units already in the game; I made a footman into a worker unit and it received duplication.
  • Only the specific unit we ordered via the trigger is actually given an order to open the build menu; the others do it without an issued order that you can track. This is the same behavior as when you press the build button manually (doesn't trigger the issued order event). Even though the dupe order is not actually issued to them the build menu opening is duplicated to units that are paused when the build order is issued.
Then I went down the rabbit hole:
  • Actually you can get all of this weirdass behavior by simply having a builder unit selected with it's build menu open, then selecting a different unit via triggers (you either get blank card or build menu from this).
  • Clearing the player's selection via triggers does not prevent this. Clearing selection and then waiting an arbitrary amount of time (I tested with 10s) does not prevent this. Removing or killing the unit (to force a pseudo-deselect) does not prevent this. The player selecting anything else or closing the build window during this wait does prevent this.
  • The issue you described above where remaking the unit after the order has been issued still caused the build menu to appear is because the of the selection-change-via-triggers-while-build-menu-is-open thing detailed above.

In my opinion this is 100% a bug with how selection and selection via code is implemented within the game engine... mixed with the build order not being properly localized to unit it's being issued to, which is also a bug within the game engine. Will Blizzard ever fix these things? Probably not because since triggers are necessary for this to occur it can't be abused/fucked with in Melee maps. That being said here's my writeup:

  1. Ordering any unit capable of building to use any of the 5 build orders will also duplicate this build menu opening to any other units currently selected by all players (AI and Neutrals excluded?). If a player has multiple units selected, only the current active unit will be affected.
  2. If a selected unit is not capable of building, it will completely remove all icons from the command card and disallow any hotkeys for abilities from working, including the default hold/move/attack/stop. There is no ESC cancel button (pressing escape does nothing) and the unit may only be issued the "smart" order by its owner (and only via right-click). Other orders may be given manually with triggers, and abilities can be activated as normal this way.
  3. The state described in 2 persists until the unit is deselected, something else is selected by/for the player instead, or the player/trigger adds additional units to their selection.
  4. If a unit's build menu is open and that player's selection is changed via triggers, this bugged build menu/blank command card state will occur, even if selection was manually cleared by a trigger first and waits are involved. If the selection is simply added to, then the previously selected unit will be bugged; if the selection is changed, the newly selected unit will be bugged.
  5. Effectively, you can hide any units command card whenever you want by opening a build menu and then selecting a unit for that player. But it doesn't persist through the player selecting anything else. Kind-of like a pseudo-Ward classification.

Unanswered questions:
  • What does an AI player do when its build menu is forced in this way? For that matter can you force UI keys for AI player slots?
  • I did not test this with spellbooks at all. I would not be surprised to learn that everything detailed in this post applies to/with units with/without spellbooks as well.
  • God help us if the learn hero ability 'sub-menu'/spellbook is similarly affected, which I suspect it might as well.

A way around this might be to force the UI key for the build menu instead of ordering the unit to build. Last time I looked into forcing UI key shenanigans the game needed at least 1 frame to redraw the UI and allow for a subsequent UI key force if the ui keys weren't both present in the same 'menu' (and being in the targeting phase of a spell cast counts as a different menu), so since you're forcing Escape in the same trigger without a wait you might not be able to immediately force B. There are ways around this but they're annoying.
 
Last edited:
Level 19
Joined
Jan 3, 2022
Messages
320
Great work, bad Bliz. The reason I dismissed ForceUICancel/ForceUIKey above: it doesn't work with hotkey layouts. ForceUICancel is hard-coded to send ESCAPE (breaks if player has grid layout enabled) and to use ForceUIKey meaningfully you need to know player's hotkey for an ability. But you can't find it out.
  1. Extracting hotkey from tooltip doesn't work (grid hotkeys are added to tooltip as part of UI, BlzGetAbilityTooltip() only returns the original WorldEdit text)
  2. I couldn't get WorldEdit's "Text - Hotkey - Normal" aka 'ahky' (Hotkey) value using natives (like BlzGetAbilityInteger/StringField)
The only solution is a fully custom build menu made with abilities?..
 
Level 39
Joined
Feb 27, 2007
Messages
4,994
Can you place the build menu such that its grid hotkey is always the same? You could just force both as long as the assumed grid hotkey isn’t also a hotkey for a building. Force B then GridKey right after; if it still needs a frame to update the gridkey should do nothing; if B did nothing because grid layout is enabled then the gridkey force should go through.
 
Level 5
Joined
Jan 23, 2020
Messages
86
Thanks for the write up @Pyrogasm

Unfortunate that there is no realistic work-around for this, as its a matter of the game engine around the build function + triggers = broken.

I'll try another approaches, that avoid the dreaded Build - issued order, but its not looking hopeful. Appreciate the help too @Luashine
 
Level 19
Joined
Jan 3, 2022
Messages
320
@Pyrogasm Possible layouts: default (classic), grid, custom. I don't like this because you cannot possibly account for a custom keybind for the build command (should be a common rebind). Other than that getting grid+classic to work will account for 99.99% of players, but I don't like random chances :peasant-sad:
 
Level 39
Joined
Feb 27, 2007
Messages
4,994
That doesn't seem inconsistent with what I found, but maybe I just don't understand. It also will fail if the player has customized hotkeys or is using the grid setup. I addressed this possible solution above:
Can you place the build menu such that its grid hotkey is always the same? You could just force both as long as the assumed grid hotkey isn’t also a hotkey for a building. Force B then GridKey right after; if it still needs a frame to update the gridkey should do nothing; if B did nothing because grid layout is enabled then the gridkey force should go through.
Also the 0.00 wait isn't 0, it's like 0.27 or something.
 
Status
Not open for further replies.
Top