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

Need Some Jass Help--Expected "Returns"

Status
Not open for further replies.
Level 3
Joined
May 24, 2016
Messages
22
So I'm trying to create an area of effect polymorph that is MUI. So I put together this script. But I'm having issues.

  • MassSheepify Copy
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Mass Sheepify
    • Actions
      • Custom script: local unit MassSheepDummy = CreateUnit( GetOwningPlayer(GetTriggerUnit()), 'u000', GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit()), bj_UNIT_FACING )
      • Custom script: local integer MassSheepLevel = GetUnitAbilityLevel(GetTriggerUnit(),'A01I')
      • Custom script: local location MSCaster = GetUnitLoc(GetTriggerUnit())
      • Custom script: call UnitApplyTimedLife(MassSheepDummy, 'BTLF', 7.00)
      • Custom script: call UnitAddAbility( MassSheepDummy, 'A01J' )
      • Custom script: call SetUnitAbilityLevel(MassSheepDummy,'A01J',MassSheepLevel)
      • Custom script: function MassSheepTargetConditions takes Nothing returns Boolean
      • Custom script: return ( IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(GetTriggerUnit()) == true ) and IsUnitStructure(GetFilterUnit()) ==false and IsUnitMechanical(GetFilterUnit()) ==false
      • Custom script: endfunction
      • Custom script: function MassSheepAction takes nothing returns nothing
      • Custom script: local unit Target
      • Custom script: local group MassSheepTargets = CreateGroup()
      • Custom script: call GroupEnumUnitsInRangeLoc(MassSheepTargets, MScaster, 450, Condition(function MassSheepTargetConditions))
      • Custom script: loop
      • Custom script: set Target = FirstOfGroup(MassSheepTargets)
      • Custom script: exitwhen Target = null
      • Custom script: call IssueTargetOrderById(MassSheepDummy,852074,Target)
      • Custom script: call GroupRemoveUnit(MassSheepTargets,Target)
      • Custom script: endloop
      • Custom script: call RemoveLocation(MScaster)
      • Custom script: call DestroyGroup(MassSheepTargets)
      • Custom script: set MScaster=null
      • Custom script: set Target=null
      • Custom script: set MassSheepTargets=null
      • Custom script: endfunction
      • Custom script: set udg_MassSheepGroup = null
      • Custom script: set udg_MassSheepifyCaster = null
      • Custom script: set udg_MassSheepDummy = null
      • Custom script: set MassSheepDummy = null
Whenever I try to test it, I get an "expected returns" syntax error at this starting line. Can somehow help me understand why?

  • Custom script: function MassSheepTargetConditions takes Nothing returns Boolean
  • Custom script: return ( IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(GetTriggerUnit()) == true ) and IsUnitStructure(GetFilterUnit()) ==false and IsUnitMechanical(GetFilterUnit()) ==false
  • Custom script: endfunction

Also, since I haven't been able to test it, will the rest of this trigger function?
 
Level 12
Joined
Feb 22, 2010
Messages
1,115
What is the point of writing with full custom scripts instead of jass directly (for this spell)? Its better to write this in pure GUI or jass.

Also nested functions are not allowed. You must terminate a function before you declare a new one.
 
Level 39
Joined
Feb 27, 2007
Messages
5,016
What Ceday said about nested functions is the real issue here. What you’re doing is simply not going to work. Only the actions in the topmost function will be executed when the trigger runs. Any other function will have to be called by .execute() .evaluate() or ExecuteFunc() since they are below the top function in the map script and JASS can only reference functions above it.
 
Level 3
Joined
May 24, 2016
Messages
22
What Ceday said about nested functions is the real issue here. What you’re doing is simply not going to work. Only the actions in the topmost function will be executed when the trigger runs. Any other function will have to be called by .execute() .evaluate() or ExecuteFunc() since they are below the top function in the map script and JASS can only reference functions above it.


Could you give an example of the sort of thing I need to do to fix it? How could I make it un-nested? (I'm not a programmer in the slightest, so even what I've got there was hard to try to piece together)

I tried full GUI (And GUI+customscript), but couldn't figure out MUI way to do it (As well as it caused the ability to crash after second use and stop functioning).
 
Level 39
Joined
Feb 27, 2007
Messages
5,016
  • Events
    • Unit - A unit starts the effect of an ability
  • Conditions
    • (Ability being cast) equal to Mass Sheepify
  • Actions
    • Set TempPoint = (Target location of ability being cast)
    • Set TempP = (Owner of (Triggering Unit))
    • Set LVL = (Level of Mass Sheepify for (Triggering Unit))
    • Custom script: set bj_wantDestroyGroup = true
    • Unit Group - Pick every unit in (Units within 450.00 of TempPoint) and do (Actions)
      • Loop - Actions
        • Set TempU = (Picked Unit)
        • If (All conditions are true) then do (Then actions) else do (Else actions)
          • If - Conditions
            • (TempU is alive) equal to true
            • (TempU belongs to an enemy of TempP) equal to true
            • (TempU is a structure) equal to false
            • (TempU is mechanical) equal to false
          • Then - Actions
            • Unit - Create 1 'u001' for TempP at TempPoint facing default building facing degrees
            • Unit - Add 'A01J' to (last created unit)
            • Unit - Set level of 'A01J' for (last created unit) to LVL
            • Unit - Add a 2.00 second expiration timer to (last created unit)
            • Unit - Order (last created unit) to Human Sorceress - Polymorph TempU
          • Else - Actions
    • Custom script: call RemoveLocation(udg_TempPoint)
You can do it all with 1 dummy if the dummy is properly set up to be able to cast instantly. However, using multiple dummies is the easiest approach.
 
Level 3
Joined
May 24, 2016
Messages
22
  • Events
    • Unit - A unit starts the effect of an ability
  • Conditions
    • (Ability being cast) equal to Mass Sheepify
  • Actions
    • Set TempPoint = (Target location of ability being cast)
    • Set TempP = (Owner of (Triggering Unit))
    • Set LVL = (Level of Mass Sheepify for (Triggering Unit))
    • Custom script: set bj_wantDestroyGroup = true
    • Unit Group - Pick every unit in (Units within 450.00 of TempPoint) and do (Actions)
      • Loop - Actions
        • Set TempU = (Picked Unit)
        • If (All conditions are true) then do (Then actions) else do (Else actions)
          • If - Conditions
            • (TempU is alive) equal to true
            • (TempU belongs to an enemy of TempP) equal to true
            • (TempU is a structure) equal to false
            • (TempU is mechanical) equal to false
          • Then - Actions
            • Unit - Create 1 'u001' for TempP at TempPoint facing default building facing degrees
            • Unit - Add 'A01J' to (last created unit)
            • Unit - Set level of 'A01J' for (last created unit) to LVL
            • Unit - Add a 2.00 second expiration timer to (last created unit)
            • Unit - Order (last created unit) to Human Sorceress - Polymorph TempU
          • Else - Actions
    • Custom script: call RemoveLocation(udg_TempPoint)
You can do it all with 1 dummy if the dummy is properly set up to be able to cast instantly. However, using multiple dummies is the easiest approach.
Thanks so much +rep

Out of curiosity though, how does this stay MUI? Won't the variables conflict--or is the instant cast making it MUI?

And why set the player variable? I understand with location we're using it to stop leaks, but I thought player variables didn't leak anyway?

As for 1 Dummy, I originally had a similar GUI trigger, with a group variable (that bj wantdestroygroup is a life changer) and used 1 dummy set to a variable...but because I didn't set it to an index (and indices confuse the heck out of me in loops), it wasn't MUI and crashed. So I tried making a local variable, but then found out that local variables didn't work inside the loop. Which lead to the custom script trigger. I guess doesn't really matter, since the multiple dummies only issue is that it creates a shadow on the ground if there are a lot of units.
 
Last edited:
Level 39
Joined
Feb 27, 2007
Messages
5,016
You can and should remove the shadow from your dummy units (set shadow size to 0 in both dimensions). With no model, no attack, no shadow, and the Locust ability they should be functionally invisible and un-interactable.

I used a player variable to reduce the number of times it needed to compute Owner of (Triggering Unit) just like the TempU variable avoids querying Picked Unit repeatedly.

I have no idea what you’re saying about indices and crashing. No two triggers can run simultaneously and they execute effectively instantly without any Waits. If something a trigger does causes another trigger to fire (in this instance creating a unit runs any ‘unit enters <region>’ events and ordering the dummies to use the spell runs ‘unit issued an order targeting an object’ events), the first trigger will pause while those other triggers execute and will resume when they’re done. So if a trigger that ran on unit entering region set/changed TempU or TempP then it would mess up this trigger’s execution.
 
Level 3
Joined
May 24, 2016
Messages
22
You can and should remove the shadow from your dummy units (set shadow size to 0 in both dimensions). With no model, no attack, no shadow, and the Locust ability they should be functionally invisible and un-interactable.

I used a player variable to reduce the number of times it needed to compute Owner of (Triggering Unit) just like the TempU variable avoids querying Picked Unit repeatedly.

I have no idea what you’re saying about indices and crashing. No two triggers can run simultaneously and they execute effectively instantly without any Waits. If something a trigger does causes another trigger to fire (in this instance creating a unit runs any ‘unit enters <region>’ events and ordering the dummies to use the spell runs ‘unit issued an order targeting an object’ events), the first trigger will pause while those other triggers execute and will resume when they’re done. So if a trigger that ran on unit entering region set/changed TempU or TempP then it would mess up this trigger’s execution.

That makes sense on the player variable. Shadow is set to 0. But, it could also be something with the unit model for the casting unit. I'll double check. Could also be computer.

Indices as in, using an index to make it MUI. Counting up the index so that it uses a new entry in the index for the variable, then reset it after, so that a second trigger of the same doesn't reset the global. But as I said, indices confuse me; I prefer locals (my lack of coding experience most likely(.

I have a trigger that returns abilities, which also functions almost instantly. As such, a second trigger gets put on the stack close enough to disrupt variables. Before, my GUI used 1 dummy with a global variable, which caused the ability to not function. Don't know why, but the GUI I had before wouldn't function past 2 uses because of that issue. After that, it just wouldn't even try. And if the polymorph buff got spell-stolen or the casting unit got polymorphed, instantly lost functionality (no clue why but not occurring anymore).

As I said, configuring like how you did with separate dummies seems to not have that issue. So, it works. I'm just asking questions so I'll have a little more knowledge going forward. Thank you again Pyro!
 
Level 39
Joined
Feb 27, 2007
Messages
5,016
Indices as in, using an index to make it MUI. Counting up the index so that it uses a new entry in the index for the variable, then reset it after, so that a second trigger of the same doesn't reset the global.
As I said there are no simultaneous threads in wc3; only one trigger can run at a time. If something happens that fires events in two triggers they will run sequentially and one will not interrupt the other. Indexing spell casts is only necessary if the effects involve some sort of timing, i.e. the trigger doesn't do everything instantly. In that case you should refer to this tutorial: Visualize: Dynamic Indexing. A variable shared between a trigger and a trigger that it fires by doing something (like creating the dummies above) can be overwritten by the second trigger if the second trigger sets it so if you are having issues like that you can simply use a dedicated variable(s) for the trigger. It will not randomly 'reset' a variable just by running, that's not a thing.
I prefer locals
Every GUI variable is global unless you shadow it locally. If you are bothered by this I suggest you learn Lua or JASS as they are actually very simple.
I have a trigger that returns abilities, which also functions almost instantly. As such, a second trigger gets put on the stack close enough to disrupt variables.
What does "returns" mean here? Aside from the fact that a CPU takes a finite amount of time to perform each operation, there is no "almost instantly" in wc3 triggers; they execute as fast as possible and do not delay steps unless you put Wait commands in them. It's not possible for a trigger to interrupt another trigger except by the method I detailed above. I realize something was going wrong with your setup before but it's not like "oh no somehow the variable's value changed"... there is always a specific reason something happens and you just have to know the rules.
 
Level 3
Joined
May 24, 2016
Messages
22
As I said there are no simultaneous threads in wc3; only one trigger can run at a time. If something happens that fires events in two triggers they will run sequentially and one will not interrupt the other. Indexing spell casts is only necessary if the effects involve some sort of timing, i.e. the trigger doesn't do everything instantly. In that case you should refer to this tutorial: Visualize: Dynamic Indexing. A variable shared between a trigger and a trigger that it fires by doing something (like creating the dummies above) can be overwritten by the second trigger if the second trigger sets it so if you are having issues like that you can simply use a dedicated variable(s) for the trigger. It will not randomly 'reset' a variable just by running, that's not a thing.

Every GUI variable is global unless you shadow it locally. If you are bothered by this I suggest you learn Lua or JASS as they are actually very simple.

What does "returns" mean here? Aside from the fact that a CPU takes a finite amount of time to perform each operation, there is no "almost instantly" in wc3 triggers; they execute as fast as possible and do not delay steps unless you put Wait commands in them. It's not possible for a trigger to interrupt another trigger except by the method I detailed above. I realize something was going wrong with your setup before but it's not like "oh no somehow the variable's value changed"... there is always a specific reason something happens and you just have to know the rules.


Returns as in throws back the same ability. So it sets a second run of the trigger But that means it's using the same trigger to return (e.g., rewriting the variable). So short answer, yes, it's a second trigger that ultimately rewrites the variable by doing a second run of the first trigger alongside it (because both trigger off the starts the effect of the base channel ability).

But, it's fixed now I think. I also tinkered with the return trigger to help smooth that out.
 
Last edited:
Status
Not open for further replies.
Top