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

Make an item uninteractable

Status
Not open for further replies.
Level 5
Joined
Dec 29, 2008
Messages
61
I'm trying to make a particular item non-interactable if the player isn't on a particular quest step. I thought it would be simple enough but the unit (a Hero if that matters) does not stop moving to interact with it. It does, however, lose its current selection.

  • Events
    • Unit - A unit Is issued an order targeting an object
  • Conditions
    • (Item-type of (Target item of issued order)) Equal to Stolen Sack
  • Actions
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • StolenSacksQuestStep[PlayerNumber] Not equal to 1
      • Then - Actions
        • Selection - Clear selection for mPlayer[PlayerNumber].
        • Unit - Order (Ordered unit) to Stop.
      • Else - Actions
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,544
You haven't Set PlayerNumber anywhere in this trigger therefore it's not going to work in multiplayer. Understand that PlayerNumber is a global variable that can only have one value at a time. So for example when you Set this variable in another trigger like so:
  • Events
    • Unit - A unit dies
  • Conditions
  • Actions
    • Set Variable PlayerNumber = (Player number of (Owner of (Dying unit))
It's going to remain at that value until you Set it to something else.

Now let's say that PlayerNumber was last Set to 2 because a unit owned by Player 2 died. Your Item trigger would now read like this:
  • Events
    • Unit - A unit Is issued an order targeting an object
  • Conditions
    • (Item-type of (Target item of issued order)) Equal to Stolen Sack
  • Actions
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • StolenSacksQuestStep[2] Not equal to 1
      • Then - Actions
        • Selection - Clear selection for mPlayer[2].
        • Unit - Order (Ordered unit) to Stop.
      • Else - Actions
Note the [2]. Nowhere in this trigger are you updating the value of PlayerNumber to be equal to the Player Number of the Ordered unit, therefore this trigger is still using it's last set value (2) and will only work for Player 2. Also, understand that the PlayerNumber variable is just a tool for making the trigger more efficient/easier to work with. There's no reason you couldn't just use the function (Player number of...) inside of your variable [index].

Anyway, to answer your question. You can't issue Orders in response to an issued Order. One solution I've used in the past is to Stun/Unstun the unit like so:
  • Events
    • Unit - A unit Is issued an order targeting an object
  • Conditions
    • (Item-type of (Target item of issued order)) Equal to Stolen Sack
  • Actions
    • Set Variable PlayerNumber = (Player number of (Owner of (Ordered unit)))
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • StolenSacksQuestStep[PlayerNumber] Not equal to 1
      • Then - Actions
        • Unit - Order (Ordered unit) to Stop.
        • Custom script: call BlzPauseUnitEx(GetOrderedUnit(), true)
        • Custom script: call BlzPauseUnitEx(GetOrderedUnit(), false)
      • Else - Actions
I can't guarantee that this works though and this may cause some odd problems. Also, it's not stopping other Players from giving you the Stolen Sack, unless the Item is Undroppable or something. What people usually do is use the Acquire Item event and force the Item to be dropped the moment it's acquired.
 
Last edited:
Level 5
Joined
Dec 29, 2008
Messages
61
I do have PlayerNumber stored as an array that can be referenced however I just thought I didn't need to do that for this because the trigger was just about units, but thanks for this.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,544
PlayerNumber isn't stored as an Array but I understand why you got confused.

So to break it down, PlayerNumber is an Integer variable, no more, no less.

What you're doing is using it as the [index] of your Array variables. In this case it's being used as the index for StolenSacksQuestStep[] and mPlayer[].

The index is the Integer that goes into the brackets of your Array variable --> [ index goes here ]

With that in mind, the rule is simple. You need to ALWAYS Set PlayerNumber before referencing it. If you don't then you'll have no control over it's value and your trigger will only work for the Player that it was last set to. The reason it would work without issues in your singleplayer tests is because you, Player 1, are the only one ever firing any triggers, therefore PlayerNumber is always Set to 1. The moment another Player joins the game and introduces a different Player number you'll begin running into the issues I've described before.

However, you should also understand that you don't NEED to use a variable as the index. You can write the value yourself or use some other function:
  • Player - Add 100 gold to mPlayer[1]
The reason for the variable is to improve the efficiency, simplicity, and organization of your triggers.

For example, this trigger will work for any number of players just fine:
  • Events
    • Unit - A unit dies
  • Conditions
  • Actions
    • Unit - Create 1 Footman for mPlayer[Player number of (Owner of (Dying unit))]
    • Player - Add 100 gold to mPlayer[Player number of (Owner of (Dying unit))]
    • Player - Add 50 lumber to mPlayer[Player number of (Owner of (Dying unit))]
However, this trigger which does the same thing is more efficient and easier to setup (at least once you get used to the pattern):
  • Events
    • Unit - A unit dies
  • Conditions
  • Actions
    • Set Variable PlayerNumber = (Player number of (Owner of (Dying unit)))
    • Unit - Create 1 Footman for mPlayer[PlayerNumber]
    • Player - Add 100 gold to mPlayer[PlayerNumber]
    • Player - Add 50 lumber to mPlayer[PlayerNumber]
Regarding efficiency, this line: (Player number of (Owner of (Dying unit))) is actually three function calls, meaning that the game has to basically look up three different things each time you use it. First it's getting the (Dying unit), then it's getting the Player that owns that unit, then it's getting the Player Number of that Player. It's doing this tedious process three separate times throughout the first trigger.

However, by setting the Player number to a variable, we reduce the number of times this tedious process happens by 2. Instead, the game can simply look up PlayerNumber which is an easier process. Again, this design style is OPTIONAL but it's a good idea if you want clean and efficient triggers.

There is one last important thing to know though. There are very rare cases where you may want to use a different variable than PlayerNumber. The biggest culprit and maybe the only culprit for this problem is the Unit - A unit dies Event which unlike other Events can insert it's trigger directly into the middle of your other triggers, potentially causing your shared variables between the two to change values and create unwanted end results. The normal behavior that most Events have is that their triggers run AFTER the trigger that set them off resolves it's last Action (or when a Wait occurs).

Long story short, use a different version of PlayerNumber inside of triggers that use the A unit dies Event. Also, if you experience any "wrong player" bugs in your other triggers then doing this may help resolve the issue.
 
Last edited:
Status
Not open for further replies.
Top