• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[Trigger] Trigger creates multiple units when cast by a unit group

Status
Not open for further replies.
Level 2
Joined
Feb 29, 2012
Messages
8
I have created (read found here and copied) a trigger to find the nearest building, move my unit there, kill it, pay its price and train a different unit. It works when a single unit uses the spell, but when I make a group use it, the killing part is skipped - it takes the gold for training, trains a unit for each unit in the group, but kills only one of the units. Why?
  • Equip
    • Events
      • Unit - A unit Started using ability
    • Conditions
      • (Ability being cast) Equals Equip (Runaway)
      • (Unit-type of (Casting unit)) Equals Runaway
    • Actions
      • Set TempLocA = (Position of (Casting unit))
      • Custom script: set bj_wantDestroyGroup = true
      • Unit Group - Pick every unit in (Units owned by (Owner of (Triggering unit)) matching ((((Matching unit) is alive) Equals TRUE) and ((Unit-type of (Matching unit)) Equals Runaway Hideout))) and do (Actions)
        • Loop - actions
          • Set TempLocB = (Position of (Picked unit))
          • Set TempRealA = (Distance between TempLocA and TempLocB)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - conditions
              • TempRealA Less than TempRealB
            • Then - actions
              • Custom script: set udg_TempUnitA = null
              • Set TempUnitA = (Picked unit)
              • Set TempRealB = TempRealA
            • Else - actions
          • Custom script: call RemoveLocation(udg_TempLocB)
      • Custom script: call RemoveLocation(udg_TempLocA)
      • Unit - Order (Casting unit) to Move to (Position of TempUnitA)
      • Wait until ((Distance between (Position of (Casting unit)) and (Position of TempUnitA)) Less or equal to 256.00), checking every 0.15 seconds
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - conditions
          • (((Owner of TempUnitA) Gold) More or equal to 125) and (((Owner of TempUnitA) Lumber More or equal to 30)
        • Then - actions
          • Unit - Hide (Casting unit)
          • Unit - Add Equip (Charge for) to TempUnitA
          • Unit - Kill (Casting unit)
          • Unit - Order TempUnitA to Far Seer - Spirit Wolf
          • Unit - Remove Equip (Charge for) from TempUnitA
          • Player - Make Wildling available for training/construction by (Owner of (Casting unit))
          • Unit - Order TempUnitA to train/upgrade to a Wildling
          • Player - Make Wildling unavailable for training/construction by (Owner of (Casting unit))
        • Else - actions
I have Czech edition of Warcraft with the localized version of the Editor, so some commands in the trigger might have been wrongly translated, so just assume they are what you think they are.
 
Level 2
Joined
Feb 29, 2012
Messages
8
Thank you, I will try to edit the trigger tomorrow and then edit this comment with the result or additional questions.
 
Level 45
Joined
Feb 27, 2007
Messages
5,578
Custom script: set udg_TempUnitA = null is completely unnecessary. The reason it only works for one unit is that you never reset TempRealB to some 'arbitrary large maximum distance'. When the second unit gets picked its distance to the closest Runaway Hideout is never closer to the hideout than the previous unit's closest distance so TempUnitA is never set. Right before your Unit Group - Pick... put Set TempRealB = 100000.00
 
Level 2
Joined
Feb 29, 2012
Messages
8
  • ...
  • Set IndexWrite = (IndexWrite + 1)
  • Set CasterArray[IndexWrite] = (Casting unit)
  • Wait until ((Distance between (Position of CasterArray[IndexWrite]) and (Position of TempUnitA)) Menšà nebo rovno 256.00), checking every 0.15 seconds
  • Set IndexRead = (IndexRead + 1)
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • ...
This is the new trigger, it now "eats" all the units, but still waits for the last unit to come to the building. I know why that happens, but I don't have a clue how to solve it. I tried doing this:
  • ...
  • Set IndexWrite = (IndexWrite + 1)
  • Set CasterArray[IndexWrite] = (Casting unit)
  • Set ArrayIndex[IndexWrite] = IndexWrite
  • Wait until ((Distance between (Position of CasterArray[ArrayIndex[1]]) and (Position of TempUnitA)) Menšà nebo rovno 256.00), checking every 0.15 seconds
  • Set IndexRead = (IndexRead + 1)
  • Custom script: call RemoveLocation(udg_ArrayIndex[1])
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • ...
but that won't compile, because of errors caused by the Custom script (there is no error with it, but it, for some reason breaks other parts of the code). Also, that might not work based on how the arrays work - if I delete an index, do the following collapse or do I just leave a blank space?

Edit: I also solved what Pyrogasm said, but there's no need to show that.
 
Level 45
Joined
Feb 27, 2007
Messages
5,578
Since that CS line is syntax-correct it probably has an invisible character somewhere in in. Delete the action and make it anew; it should compile then. Arrays don't shift automatically, but there are things like linked lists available through vJASS if you are interested.

Generally speaking the Wait for <Condition> action is not recommended. The wait can mess with your variables by allowing them to get overwritten by other instances of the trigger or can simply stop some event responses from working. It also leaks 2 locations every time the condition is checked and there is NO way you can get around that if you want to use the action. The alternative has always been timers with the relevant data attached which is pretty clunky in GUI. You could probably set up some sort of dynamic indexing like you tried but that will also get a little messy. If you are okay with leaking a lot of locations then do this:

I think I see what you're trying with IndexWrite/Read but you've not implemented it properly: in the Wait line the index [1] is used where it should be [IndexWrite]. Ultimately though you're storing the wrong thing: Triggering Unit will work after the Wait and is unique for each cast of the spell... but TempUnitA is a shared global variable that keeps getting overwritten each cast. Ideally you'd store the target building for each one locally for each cast, but because of how GUI does the Wait for <Condition> you wouldn't be able to use a local variable in the comparison anyway. IMO your only real solution is to use a hashtable and briefly store the unit.
  • -------- At map init --------
  • Hashtable - Create new hashtable
  • Set YourHash = (Last created hashtable)
  • -------- In your trigger --------
  • Hashtable - Store TempUnitA as Key(TargetBuilding) of Key(Triggering Unit) in YourHash
  • Wait Until (Distance between (Position of TempUnitA) and (Position of (Triggering Unit))) less than or equal to 256.00 checking ever 0.15 seconds
  • Hashtable - Clear all child hashtables of child Key(Triggering Unit) in YourHash //Flushes the stored data so it doesn't accumulate and degrade game performance over time

If you don't want to leak locations I can show you how to do it with timers, or you can remake the Wait until <Condition> yourself manually and use a local variable:
  • -------- Very first line of the trigger! --------
  • Custom script: local unit u = null
  • -------- In your trigger --------
  • Custom script: set u = udg_TempUnitA
  • Custom script: loop
  • Custom script: set udg_TempUnitA = u
  • Set TempLocA = Position of (Triggering Unit)
  • Set TempLocB = Position of (TempUnitA)
  • Set TempReal = (Distance between TempLocA and TempLocB)
  • Custom script: call RemoveLocation(udg_TempLocA)
  • Custom script: call RemoveLocation(udg_TempLocB)
  • If (All Conditions are true) then do (Actions) else do (Actions)
    • If - Conditions
      • TempReal less than or equal to 256.00
    • Then - Actions
      • Custom script: exitwhen true
    • Else - Actions
      • Wait - 0.15 seconds //it will wait slightly longer than this
  • Custom script: endloop
  • -------- At this point TempUnitA has been reset to the proper unit stored in the local variable u and will be correct until you wait again further down in the trigger (if you need to for some reason) --------
  • -------- Do your stuff here --------
 
Level 2
Joined
Feb 29, 2012
Messages
8
I did try remaking the CS a few times to no avail. I'm used to Python, I think arrays shift there. I might look into vJASS later if GUI becomes too much of an obstacle.

It was meant to work if the arrays did shift, but I also see the problem there. The TempUnitA can get overwritten right now, efficiency is not my main concern as long as they find a Hideout to change. Though splitting the group would look kinda sexy if you had multiple Hideouts.

Thank you, I'll try it tomorrow and come back if I have additional problems.
 
Level 45
Joined
Feb 27, 2007
Messages
5,578
I might look into vJASS later if GUI becomes too much of an obstacle.
If you are comfortable with Python I suggest you make the switch to vJASS now. You gain an incredible amount of functionality and the syntax is not in any way complicated; like taking your training wheels off when you first can vs. waiting 5 years to do it. Python and JASS are super, super similar and vJASS essentially just adds on more OOP dot syntax (+ a bunch more specialized stuff you can read about if you actually need it).

Common.j source (all native JASS functions besides those in common.ai)
Blizzard.j source (all of Blizzards shitty BJ wrapper functions that GUI uses)
Common.ai (you will have to redeclare any of these natives you wish to use, but it can be good reference. For instance native UnitAlive is pretty helpful)

Slightly out of date JASSHelper manual (up-to-date one comes as a .html file when you download the JNGP)
fficiency is not my main concern as long as they find a Hideout to change
Not sure if it's what you're referring to but the location leaks in the Wait for <Condition> aren't just inefficient, they're actual memory hogs and will actively reduce the map's framerate/playability over time if left unattended.
 
Level 2
Joined
Feb 29, 2012
Messages
8
It works as it should now. I'll look into vJASS then :).
Not sure if it's what you're referring to but the location leaks in the Wait for <Condition> aren't just inefficient, they're actual memory hogs and will actively reduce the map's framerate/playability over time if left unattended.
I meant as in finding multiple Hideouts if the units in my group are closer to different Hideouts. I understand that memory leaks are a bad problem.

Can I have another question?

I want this to be cancellable. Right now when I cancel the training, I get money back, which is nice, but I'd like to get the Runaway back. How would I go about that?
 
Level 45
Joined
Feb 27, 2007
Messages
5,578
No, no questions!

You can probably get that to work but it will be complicated because there's no event that fires when you cancel a unit. Afaik it doesn't trigger any orders (numeric or otherwise) or do anything else detectable. There are a couple workarounds I can think of that may work (run a low-period timer to detect when you regain 125G/30L, periodically check the current order of the Hideout to make sure it's still training them, use something like TrainingDetection v2.1 to keep track of the build queue and what's in it) but they will require more in-depth triggers (or vJASS).

Ultimately there is probably not going to be a way to get the specific unit you saxrificed back (correct HP/mana, items, etc.). The best you'll be able to do is make a new one, which means you could potentially exploit it to heal your units. Maybe with a hashtable you'd be able to link the sacrificial unit and the yet-to-be-constructed unit in the build queue, but again I'm not convinced.
 
Status
Not open for further replies.
Top