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

Simple GUI Trigger

Status
Not open for further replies.
Level 14
Joined
Oct 16, 2011
Messages
296
Hey guys! Uhm, I want to try to make a GUI spell where peons, peasants, and other workers can build structures BUT with new different costs. But sadly, I can't make it properly. So, maybe someone out there can help me with this?

ex. A peon wants to construct a barracks w/ 90 hp cost. If the peon has more than 90 hp, he will construct it (health is deducted initially). If he doesn't have enough health, then he will stop and a MSG will display; "Health is not enough."
If he cancels the construction, he will regain 75% of his hp cost.


An easier request, fellow mortals. I need a detecting system that whenever a structure begins construction, the unit building it will be detected. (Can only be used in Human, Orc and Undead type construction). Why I need this?
Because this sucks:
  • Unit - A unit Begins construction
THIS MUST ONLY BE AN INITIAL-LIKE TRIGGER
NO NEED FOR CANCEL/FINISH STRUCTURE TRIGGERS
 
Last edited:
Level 9
Joined
May 21, 2014
Messages
580
Why does Unit - A unit Begins construction suck? I don't get it.

Initial-like trigger? Can you define more on that?

If you only want to get the unit of the building being constructed, then use:
Unit - A unit Begins construction
then refer to Event Response - Constructing Structure
Constructing Structure is the unit building that you want.
 
Level 14
Joined
Oct 16, 2011
Messages
296
No, milord.
  • Unit - A unit Begins construction
This Event only gets the structure that begins its construction, but not the worker that begins constructing it (maybe because its not applicable for NE's wisps that's why Blizzard didn't allow this.)

There is also no Event like this:
Unit - Specific Unit Begins construction
So, I need a trigger that the function would be like this Event above where the Specific Unit is the worker.
 
Level 9
Joined
May 21, 2014
Messages
580
No, milord.
  • Unit - A unit Begins construction
This Event only gets the structure that begins its construction, but not the worker that begins constructing it (maybe because its not applicable for NE's wisps that's why Blizzard didn't allow this.)

There is also no Event like this:
Unit - Specific Unit Begins construction
So, I need a trigger that the function would be like this Event above where the Specific Unit is the worker.

I have read threads regarding this matter.

It seems that there is no way to detect the worker accurately since each race has a different way of building their structure. Peasants can build together one structure, acolytes can build without actually working on the building, etc...

Using order events won't be accurate as well. JASS might find the way, though...
 
Level 14
Joined
Oct 16, 2011
Messages
296
I saw those threads too, but it seems like their way they want their units to be detected is loop or finished construction event. But for me, I want a trigger that detects it on the beginning of construction, but if the construction started, the unit will be removed in the detection. (Did you understand me? lol I dunno what to say.)

So, even if Peasants, Acolytes and Peons have different way of building, it will still run.

For example, a peasant wants to construct a farm. Before the construction begins, the trigger will detect it (and I will apply new things like farm will have hp cost, so the peasant will have its hp reduced instantly) and after the construction begins it will then be removed from detection. So, this means no need to detect many peasants that is issued to help the main peasant 'coz the main peasant has already been detected and removed. Same with Acolytes, they will only be detected before summoning their structures, but will be removed from detection after construction begins. (Did you get me? I guess you won't understand me :( )
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
But then you have it when it is ordered to build it.
It then has to walk to that location and build the structiure.
He wants to have the moment when he is building the structure.

I do have one option... Create an ability for every structure and order the unit to build when he has cast the ability.
The problem is collision of buildings.
 
Level 25
Joined
May 11, 2007
Messages
4,651
ex. A peon wants to construct a barracks w/ 90 hp cost. If the peon has more than 90 hp, he will construct it (health is deducted initially). If he doesn't have enough health, then he will stop and a MSG will display; "Health is not enough."
If he cancels the construction, he will regain 75% of his hp cost.


Would be easier if you disabled the building from the start from the peon, checking every 1 second if the peon has enough health for building said buildings, if it has you enable those buildings/the abilites they are made from, and disable those that the peon doesn't have enough health for.

For example you could use the "tiny townhall" and "tiny farm" abilities that you could add to your peon when his health is high enough.
 
Level 14
Joined
Oct 16, 2011
Messages
296
You can get the construction order using "unit is issued an order targeting a point", checking if the order string is equal to the building's unit string, e.g. "farm".
I already know about that.
The problem is, the trigger will run while the unit is walking towards its targeted point.
But then you have it when it is ordered to build it.
It then has to walk to that location and build the structiure.
He wants to have the moment when he is building the structure.

I do have one option... Create an ability for every structure and order the unit to build when he has cast the ability.
The problem is collision of buildings.
I think structures can't cast an ability when its construction is not yet complete? Correct me if I'm wrong.
Would be easier if you disabled the building from the start from the peon, checking every 1 second if the peon has enough health for building said buildings, if it has you enable those buildings/the abilites they are made from, and disable those that the peon doesn't have enough health for.

For example you could use the "tiny townhall" and "tiny farm" abilities that you could add to your peon when his health is high enough.
Thanks man, but I strikethrough-ed those sentences. I can do those parts, the problem is the detection :goblin_cry:
 
Level 14
Joined
Oct 16, 2011
Messages
296
Well if you do those parts, then you don't need to detect the worker.
Sometimes, you gotta work around the problems when ya can't solve them.

Yes, that should work. BUT (yes, a big but. I love big buts) if I use the "health is enough enable/disable" you're suggesting, then I still can't apply the HP Cost :goblin_cry: I could just allow the worker to build the structure if his health is greater than the required hp, but no HP Cost could be applied. Why? because the worker is not detected.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
You could always use an Order Tracking System.
It saves the last ... orders of every unit. (Ussually you only need 1 but I can imagine that people would like to have more.)
In that thing, you can check if any unit is ordered to construct a building of the same type as the constructed structure at the location of the constructed structure.
In that case you have an almost working "Get Constructing Unit"
The only problem would be if 2 units would try to build the same structure at the same location...

You could make more checks "Owner of structure is equal to owner of builder", "Builder is in 600 range of structure"
For humans, when they are building, they are actually "repairing".
So you can check if their current order is "repair" or <name of constructed structure>. The first is building, the second is moving towards the location.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Sorry LordDz but xyzier_24 is right.

You still have to have a moment when you must remove the health from the unit.
You still have to "Set <unit> life to (life of <unit> - cost of <building>)"
And you want that to be done at the moment that he starts the building construction.

However cancelling a structure is a bit different.
Humans are not bound to a structure so cancelling it is very unlogical to give health back.
What you have to do is save the builder as unit handle under the handle id of the structure in a hashtable.
That way, you can always get the original builder back.

To know the exact moment of building, just do what I said. It works... 100% I think... at least 99.99% because you won't be building another structure on the same spot... if you cant (which I assume) then it works 100% if you also check the player.
 
Level 14
Joined
Oct 16, 2011
Messages
296
You could always use an Order Tracking System.
It saves the last ... orders of every unit. (Usually you only need 1 but I can imagine that people would like to have more.)
In that thing, you can check if any unit is ordered to construct a building of the same type as the constructed structure at the location of the constructed structure.
In that case you have an almost working "Get Constructing Unit"
The only problem would be if 2 units would try to build the same structure at the same location...

You could make more checks "Owner of structure is equal to owner of builder", "Builder is in 600 range of structure"
For humans, when they are building, they are actually "repairing".
So you can check if their current order is "repair" or <name of constructed structure>. The first is building, the second is moving towards the location.

Looks interesting. How to do/ where can I find this System?
If it has to use hashtables, this would be difficult for me ('coz I really don't use hashtables)
Sorry LordDz but xyzier_24 is right.

You still have to have a moment when you must remove the health from the unit.
You still have to "Set <unit> life to (life of <unit> - cost of <building>)"
And you want that to be done at the moment that he starts the building construction.

However cancelling a structure is a bit different.
Humans are not bound to a structure so cancelling it is very unlogical to give health back.
What you have to do is save the builder as unit handle under the handle id of the structure in a hashtable.
That way, you can always get the original builder back.

To know the exact moment of building, just do what I said. It works... 100% I think... at least 99.99% because you won't be building another structure on the same spot... if you cant (which I assume) then it works 100% if you also check the player.

Don't worry, milord. I already have an idea how to do this cancel to regain hp spent system :thumbs_up:
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
I never used any published order systems and after a bit of search... I haven't found one either.
But IMO this kind of system should be in every map that calls itself good because you can increase gameplay massively.
I even have seen a few people use one but forgot who did.

If someone knowns one, the system basically saves the last order of every unit.
I can and will make one myself but that will be long until I even start working on it.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Why do people keep using "Game - BD_BuildEvent becomes Equal to 1.00"?
It bugs like hell.
In this case it won't matter that much but it is still opening bugs.

set to 1
(event responses are ran)
set to 0

When the event response creates the effect too, it will run that piece of code again.
This will result in setting the variable to 1 again... but it already is 1...
This time, the event doesn't run any more.
When you use "Game - BD_BuildEvent becomes Equal to 0.00", you remove that bug.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
In this system it doesn't really matters but I found this bug when trying a DDS. looking_for_help's PDD System has an example where he uses 1 as the event.
When I deal damage when that event fires, I want to have that event fired again.
However at that moment I desperately tried to figure out why in the world my triggers didn't ran the second time.

After all I never use a DDS any more and I also use better events than this so for me the problem is history :D
 
Level 14
Joined
Oct 16, 2011
Messages
296
IcemanBo it works fine for normal purpose, but I will be testing it for many times to see if there are issues. Still, thanks man. I never though I will find someone like you. Can I hug you? No? Why? Yes, you're so cool you make my summer feel like winter.

But, can you tell me what's the purpose of the custom abilities? I'm still not good at Jass (but currently reading tutorials).
 
In theory, yes. It might bug.
But in reality I did not test it enough. In a construced scenario, you probably could bug it, if you want it on purpose.
For normal usage, it will be fine. I'm pretty sure of it.

Why?
I only make something like a approximation to evaluate the most realistic unit who could be the builder.
There is not a 1oo% guarantee that it is builder in real.

If you want to know how it works, example:

- A unit gets order to make a "tower" at point x1, y1. (OrderX, OrderY)
- The unit will be saved into UnitGroup[tower].
- When a tower enters map (gets created), I check the location, and
compare the exact x/y cooridnates of tower with OrderX/Y values of all units in UnitGroup[tower].
- If there is a match, the checked unit is the builder. If there are many matches, I approximately pick the closest unit.

How can it bug?

- If there exists a unit with same orderId as the builder towards exact same location x/y of tower, and also is closer to the tower as the builder at same time. (but as I said it's not very realistic)
- If unit gets order "tower" to x/y, but before he could start you create a tower at exact same x/y via trigger, then I think the unit still will be evaluated as Builder, even there is none (but also not very realistic)
- maybe more, idk
 
Level 14
Joined
Oct 16, 2011
Messages
296
If there exists a unit with same orderId as the builder towards exact same location x/y of tower, and also is closer to the tower as the builder at same time. (but as I said it's not very realistic)

Found this as a bug where the other worker is chosen as its builder. But who would do such a thing to order two workers to build in the same spot? This would be okay :D
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
In theory, yes. It might bug.
But in reality I did not test it enough. In a construced scenario, you probably could bug it, if you want it on purpose.
For normal usage, it will be fine. I'm pretty sure of it.

How can it bug?

- If there exists a unit with same orderId as the builder towards exact same location x/y of tower, and also is closer to the tower as the builder at same time. (but as I said it's not very realistic)
- If unit gets order "tower" to x/y, but before he could start you create a tower at exact same x/y via trigger, then I think the unit still will be evaluated as Builder, even there is none (but also not very realistic)
- maybe more, idk

I said that this one shouldn't matter that much if you meant the event bug.
But systems where the event could run again when the event is still running, then it bugs... Damage Detection Systems, Order Tracking Systems, Missile Systems and some more systems are able to create that bug... but it is still bad to use 1 instead of 0 because you create the possibility of the bug and you learn the user to do something wrong.

About the bugs you mentioned... it sounds like you have a 99.99% chance that it will not bug so I wouldn't really care much about them.
However when you do keep track of the orders, no data in the hashtable will leak and you will have 100% ensurance...
I wonder why noone has uploaded any Order Tracking System so far.
 
Level 14
Joined
Oct 16, 2011
Messages
296
I also taught this is easy, but when I tried to use this;
  • Test
    • Events
      • Unit - A unit Is issued an order targeting a point
    • Conditions
      • (Issued order) Equal to (Order(wyverncage1))
    • Actions
      • Unit - Order (Triggering unit) to Stop
I ordered the unit to stop if ordered to construct a structure using 'wyverncage1' data, but didn't stop
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
I know the problem.

It is hardcoded into the game:

A unit gains an order.
The event "A unit is issued an order" is fired.
The unit changes order to the given order.

When you change the order to stop when the event is fired, your order will be overwritten immediately after that.
That is why you need a 0 seconds timer or someway to stop the order from the unit... which is as far as I know impossible.

You can use this:
JASS:
globals
    hashtable udg_DelayedOrder_Hashtable        = InitHashtable()
    integer udg_DelayedOrder_TypeTarget         = 1
    integer udg_DelayedOrder_TypePointTarget    = 2
    integer udg_DelayedOrder_TypeTrain          = 3
    integer udg_DelayedOrder_TypeNoTarget       = 4
endglobals

library delayedorder
    function IssueOrderCallback takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local integer id = GetHandleId(t)
        local unit whichUnit = LoadUnitHandle(udg_DelayedOrder_Hashtable, id, 0)
        local integer orderType = LoadInteger(udg_DelayedOrder_Hashtable, id, 1)
        local string order = LoadStr(udg_DelayedOrder_Hashtable, id, 2)
        local widget targetWidget
        local location targetLocation
        local integer unitTypeId
        
        if orderType == udg_DelayedOrder_TypeTarget then
            set targetWidget = LoadWidgetHandle(udg_DelayedOrder_Hashtable, id, 3)
            call IssueTargetOrder(whichUnit, order, targetWidget)
            set targetWidget = null
        elseif orderType == udg_DelayedOrder_TypePointTarget then
            set targetLocation = LoadLocationHandle(udg_DelayedOrder_Hashtable, id, 3)
            call IssuePointOrder(whichUnit, order, GetLocationX(targetLocation), GetLocationY(targetLocation))
            call RemoveLocation(targetLocation)
            set targetLocation = null
        elseif orderType == udg_DelayedOrder_TypeTrain then
            set unitTypeId = LoadInteger(udg_DelayedOrder_Hashtable, id, 3)
            call IssueImmediateOrderById(whichUnit, unitTypeId)
        elseif orderType == udg_DelayedOrder_TypeNoTarget then
            call IssueImmediateOrder(whichUnit, order)
        endif
        
        call FlushChildHashtable(udg_DelayedOrder_Hashtable, id)
        call DestroyTimer(t)
        set t = null
        set whichUnit = null
    endfunction
    function IssueTargetOrderDelayed takes unit whichUnit, string order, widget targetWidget, real delay returns nothing
        local timer t = CreateTimer()
        local integer id = GetHandleId(t)
        
        call TimerStart(t, delay, false, function IssueOrderCallback)
        call SaveUnitHandle(udg_DelayedOrder_Hashtable, id, 0, whichUnit)
        call SaveInteger(udg_DelayedOrder_Hashtable, id, 1, udg_DelayedOrder_TypeTarget)
        call SaveStr(udg_DelayedOrder_Hashtable, id, 2, order)
        call SaveWidgetHandle(udg_DelayedOrder_Hashtable, id, 3, targetWidget)
        
        set t = null
    endfunction
    function IssuePointOrderDelayed takes unit whichUnit, string order, location targetLocation, real delay returns nothing
        local timer t = CreateTimer()
        local integer id = GetHandleId(t)
        
        call TimerStart(t, delay, false, function IssueOrderCallback)
        call SaveUnitHandle(udg_DelayedOrder_Hashtable, id, 0, whichUnit)
        call SaveInteger(udg_DelayedOrder_Hashtable, id, 1, udg_DelayedOrder_TypePointTarget)
        call SaveStr(udg_DelayedOrder_Hashtable, id, 2, order)
        call SaveLocationHandle(udg_DelayedOrder_Hashtable, id, 3, targetLocation)
        
        set t = null
    endfunction
    function IssueTrainOrderDelayed takes unit whichUnit, integer unitTypeId, real delay returns nothing
        local timer t = CreateTimer()
        local integer id = GetHandleId(t)
        
        call TimerStart(t, delay, false, function IssueOrderCallback)
        call SaveUnitHandle(udg_DelayedOrder_Hashtable, id, 0, whichUnit)
        call SaveInteger(udg_DelayedOrder_Hashtable, id, 1, udg_DelayedOrder_TypeNoTarget)
        call SaveInteger(udg_DelayedOrder_Hashtable, id, 3, unitTypeId)
        
        set t = null
    endfunction
    function IssueImmediateOrderDelayed takes unit whichUnit, string order, real delay returns nothing
        local timer t = CreateTimer()
        local integer id = GetHandleId(t)
        
        call TimerStart(t, delay, false, function IssueOrderCallback)
        call SaveUnitHandle(udg_DelayedOrder_Hashtable, id, 0, whichUnit)
        call SaveInteger(udg_DelayedOrder_Hashtable, id, 1, udg_DelayedOrder_TypeNoTarget)
        call SaveStr(udg_DelayedOrder_Hashtable, id, 2, order)
        
        set t = null
    endfunction
endlibrary
 
Level 14
Joined
Oct 16, 2011
Messages
296
Yes the trigger runs
  • Test
    • Events
      • Unit - A unit Is issued an order targeting a point
    • Conditions
      • (Issued order) Equal to (Order(wyverncage1))
    • Actions
      • Unit - Order (Triggering unit) to Stop
      • Game - Display to (All players) the text: it's running


Yes mate, it runs. But the unit doesn't do the "stop" order
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
I know the problem.

It is hardcoded into the game:

A unit gains an order.
The event "A unit is issued an order" is fired.
The unit changes order to the given order.

When you change the order to stop when the event is fired, your order will be overwritten immediately after that.
That is why you need a 0 seconds timer or someway to stop the order from the unit... which is as far as I know impossible.

Do I have to repeat myself?
 
Level 14
Joined
Oct 16, 2011
Messages
296
Hahaha. My laziness stops me from thinking ways to fix it.
Never thought of that, IcemanBo. Thanks dude.
I'm starting to love you. Is homosexuality okay for you? 'cos I want you so bad, man.

It actually runs, 'cos changing order while the event is running, it overwrites the new order (just like Wietlol said).
But if I pause the unit, the order will be paused too. And while the order is paused, the new order will run, and then unpause the unit.

This is what happened, right?
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Did you test it?

When I test this the unit is still walking:
  • Untitled Trigger 001
    • Events
      • Unit - A unit Is issued an order targeting a point
    • Conditions
    • Actions
      • Game - Display to (All players) the text: ((Name of (Triggering unit)) + ('s current order is + (String((Current order of (Triggering unit))))))
      • Unit - Pause (Triggering unit)
      • Unit - Order (Triggering unit) to Stop
      • Unit - Unpause (Triggering unit)
But with my solution, you just do this:
  • Untitled Trigger 001
    • Events
      • Unit - A unit Is issued an order targeting a point
    • Conditions
    • Actions
      • Set TempUnit = (Triggering unit)
      • Custom script: call IssueImmediateOrderDelayed(udg_TempUnit, "stop", 0)
You could also use "call IssueImmediateOrderDelayed(GetTriggerUnit(), "stop", 0)" instead but I kind of hate using those methods in GUI.
 
Status
Not open for further replies.
Top