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

JASS Questions

Status
Not open for further replies.
Level 12
Joined
Jan 2, 2016
Messages
973
1) Do I really need to make a seperate function for the condition of an "if"?
Example:
JASS:
function Reg_Condition takes nothing returns boolean
    local location p = GetRectCenter(udg_Reg)
    local location ul = GetUnitLoc(GetTriggerUnit())
    if ( not ( DistanceBetweenPoints(p, ul) <= 300.00 ) ) then
        return false
    endif
    return true
    set p = null
    set ul = null
endfunction

function Reg_True takes nothing returns nothing
    call AddSpecialEffectTargetUnitBJ( "overhead", GetTriggerUnit(), "Units\\Undead\\Abomination\\AbominationExplosion.mdl" )
    call DestroyEffectBJ( GetLastCreatedEffectBJ() )
endfunction

function Reg_Enter takes nothing returns nothing
    if ( Reg_Condition() ) then
        call Reg_True()
    else
    endif
endfunction

function Reg_Init takes nothing returns nothing
    set udg_Trig = CreateTrigger(  )
    call TriggerRegisterEnterRectSimple( udg_Trig, udg_Reg )
    call TriggerAddAction( udg_Trig, function Reg_Enter )
endfunction
or can it be done the "normal way". If it can, how would this trigger look like then?

2) The condition function is confusing.... if the condition is false - it returns false, ends the if and returns true.
Does it really do that or it ends the function with the "return"?

3) How do I make a trigger, that runs when a unit enters a CIRCULAR area, not rectangular?
Is it: create a dummy in it's center and make a trigger - when a unit comes in range (300) of the dummy, call the actions function?

4) Do I need to write the functions in any specific order in the Costom Script of the map, or I can do it in any order I want?
 
Level 22
Joined
Feb 6, 2014
Messages
2,466
1. No, and use TriggerAddCondition instead of TriggerAddAction if you want speed.
2. When the code encounters a return, it returns the value and immediately ends that function so return true won't execute if it executed return false
3. Periodically monitor the area.
4. Yes, you can only call functions that are already declared (without using the execute function that has a string input). If you're using JNGP, put the functions in a library so that the functions will be put at the top of the map script
 
Level 12
Joined
May 22, 2015
Messages
1,051
To expand on Flux's answer for 1:
TriggerAddCondition is all you need. You should remove TriggerAddAction and replace it with the condition. For some reason, conditions run faster than actions (I don't know all the details, but it has been tested by others).

Normally, the code inside the conditions is limited to only if statements and stuff (because it converts those functions from GUI and you can't put other ones). However, when using JASS, you can write anything you want inside trigger condition functions.

Almost all your triggers should just be one function and the InitTrig function (you will get an error if you try to save the map after deleting an InitTrig function).

JASS:
// The function will have to return boolean since it is a condition.
function myCondition takes nothing returns boolean
    // Declare variables and stuff
    local unit u = GetTriggerUnit()

    // Do something with your trigger
    call KillUnit(u)

    // Make sure to null object pointer local variables (a big oversight by creators of JASS or something - not doing this is very similar to having memory leaks)
    set u = null

    // Always return false - This indicates that the conditions failed, so the trigger's actions will not be run. The trigger has no actions anyway.
    return false
endfunction

// The default InitTrig function
function InitTrig_My_Trigger takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerAddEvent(...)
    // This is the only weird part since it adds your function as a condition instead.
    call TriggerAddCondition(t, Condition(function myCondition))
    set t = null
endfunction
 
Level 11
Joined
Dec 19, 2012
Messages
411
To expand on Flux's answer for 1:
TriggerAddCondition is all you need. You should remove TriggerAddAction and replace it with the condition. For some reason, conditions run faster than actions (I don't know all the details, but it has been tested by others).
And I gonna further elaborate your answer. The reason is TriggerAddCondition doesn't create a new thread while TriggerAddAction does.
Also, the only thing inside TriggerAddCondition can't be used, is TriggerSleepAction(), using it inside condition would break the whole thread.



Btw, function can only calls the functions on top of it, example :
JASS:
function a takes nothing returns nothing
    call b() //syntax error
    call c() //syntax error
endfunction

function b takes nothing returns nothing
    call a() //valid
    call c() //syntax error
endfunction

function c takes nothing returns nothing
    call a() //valid
    call b() //valid
endfunction
 
Level 12
Joined
May 22, 2015
Messages
1,051
Also, since the order of functions has to be like that, you should read code usually starting from the bottom.

Also, TriggerSleepAction is the Wait function in GUI. It tends to be bad for latency while playing over network. I suggest using timers, instead, which work fine inside conditions.
 
Level 12
Joined
Jan 2, 2016
Messages
973
I see, but I'm still wondering how does a function with an "if" look like..
JASS:
function Reg_Enter takes nothing returns nothing
    local location p = GetRectCenter(udg_Reg)
    local location ul = GetUnitLoc(GetTriggerUnit())
    if ( not ( DistanceBetweenPoints(p, ul) <= 300.00 ) ) then
    else
        call Reg_True()
    endif
    set p = null
    set ul = null
endfunction
Would that work? Or the call Reg_True() should be in "then"?
 
Level 12
Joined
May 22, 2015
Messages
1,051
I see, but I'm still wondering how does a function with an "if" look like..
JASS:
function Reg_Enter takes nothing returns nothing
    local location p = GetRectCenter(udg_Reg)
    local location ul = GetUnitLoc(GetTriggerUnit())
    if ( not ( DistanceBetweenPoints(p, ul) <= 300.00 ) ) then
    else
        call Reg_True()
    endif
    set p = null
    set ul = null
endfunction
Would that work? Or the call Reg_True() should be in "then"?

It depends on what you want. Programming was built to make sense to humans.

From first glance, it seems like you should just change the whole if statement part to be like:
JASS:
    if (DistanceBetweenPoints(p, ul) <= 300.00) then
        call Reg_True()
    endif

The code here does exactly what your code does. You don't always need to have an 'else' (but you always need a 'then'). What this current check does is see if the points are less than or equal to 300 away. If that is what you want to check, then you can just copy my code in since it is the same but more easy to follow and less lines of code.
 
Level 12
Joined
Jan 2, 2016
Messages
973
Okay, here comes another bunch of questions:

1) I can create multiple triggers in one trigger, right?
example:
local trigger t = CreateTrigger()
---AddConditions---
set t =null
local trigger r = CreateTrigger()
---AddCondtions---
set r = null

and so on.. Or I need to create each trigger seperately, in different InitTrig block?

2) When using local variables, do I need to make functions take them every time I call a function?
Example:
set n = GetTriggerUnit
call Some_Function(n)
~function Some_Function takes unit n returns nothing~

Or I can just call functions like if I'm using global variables, and it's ok as long as they are in the same trigger block?

3) When I use AddCondition instead of AddAction - I need to write "Condition(function ...)" instead of just "function ...". Are there any other requirements if I want to use Conditions instead of Actions? Like... do the functions need to return something (like a boolean)?

4) Can I add multiple conditions in one trigger?
Example:
local trigger t = CreateTrigger()
call AddCondition(t,Condition(function one()))
call AddCondition(t,Condition(function two()))

Or am I limited to 1 Condition + 1 Action per trigger?
 
Level 22
Joined
Feb 6, 2014
Messages
2,466
1. Yes you can create multiple triggers in a single trigger window.
2. Umm, no. Local variables does not necessarily have to be an input argument to the function.
Example:
call Some_Func(GetTriggerUnit()) <-will work
I don't quite get what you're asking.
3. If you're using JNGP, you don't have to write Condition() since JassHelper automatically does that for you. But putting one will work too.
4. I'm not 100% sure, but I think you can. The order of execution will be in the order that the conditions were added.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
1, you have to define local variables at the top and there may not be multiple variables with the same name... I guess.
2, is unclear.
3, You have to eventually, wether it is done manually by you or automatically by a pre-compiler that is out of the question. The final JASS script requires it.
4, In most cases, you want to put everything in the same thing... but yes it works.

About 2, I guess that the answer is yes and no.
Local variables are variables that are usable in the function execution where they are created.
No other function calls are going to be able to read or modify those variables.
What you can do is give those locals as parameters to a function or set them to the returned value of a function.
Variable names are also independent from other function executions.
 
Level 12
Joined
Jan 2, 2016
Messages
973
And is it okay if I have a LONG list of local variables?:
JASS:
    local unit m_ut = udg_GDD_DamageSource
    local location m_ut_pt = GetUnitLoc(m_ut)
    local unit m_targ = udg_GDD_DamagedUnit
    local location m_point = GetUnitLoc(m_targ)
    local unit p_unit = m_targ
    local location p_point = m_point
    local boolean bull = LoadBoolean(udg_Table, GetUnitTypeId(m_ut), StringHash("Upgradeable"))
    local integer targ_am = LoadInteger(udg_Table, GetUnitTypeId(m_ut), StringHash("Targets"))
    local real dist = LoadReal(udg_Table, GetUnitTypeId(m_ut), StringHash("Parabola"))
    local real rang = LoadReal(udg_Table, GetUnitTypeId(m_ut), StringHash("Range"))
    local integer a_type = LoadInteger(udg_Table, GetUnitTypeId(m_ut), StringHash("AttackType"))
Or do I need to declare all of them 1-st, and then give them the values (if the list is this long)?
 
Level 12
Joined
May 22, 2015
Messages
1,051
Multiple conditions on one trigger definitely works. I am spamming that everywhere in my map. Instead of firing a damage event, I just run a trigger (which starts with no conditions). When I want to add an effect to the damage system, I just add the condition to the appropriate trigger.

As Wietlol pointed out, it is actually more efficient to reuse triggers with the same events as much as possible. If you have an event that you are using to fire many triggers, you can instead have one trigger and assign it that event. Then you redo each trigger that runs off of that event to just add the conditions to the one you already created.

You can also add events dynamically. I just redid my hero respawn trigger (it was in GUI with ugly waits and stuff lol) and it starts with no events. Now, when a player chooses their hero, an event is added to the respawn trigger for when that hero dies.

EDIT:
Just saw your latest post. Long lists of locals like that is fine. It is mostly just a pain if you have to add some function call in between since you then have to move and change a lot of code.
 
Level 12
Joined
Jan 2, 2016
Messages
973
JASS:
function DB_Timer takes unit u, real l returns nothing
    local timer t = CreateTimer()
    call SaveUnitHandle(udg_Table, GetHandleId(t), StringHash("Unit"), u)
    call SaveReal(udg_Table, GetHandleId(t), StringHash("Health"), l)
    call TimerStart(t, 0.00, false, function DamageBlock)
    set t = null
    set u = null
endfunction

function m_main takes nothing returns nothing
    local unit m_targ = udg_GDD_DamagedUnit
    set t_life = GetUnitStateSwap(UNIT_STATE_LIFE, m_targ)
    call DB_Timer(m_targ, t_life)
// I will continiue using m_targ in this function
// function continiues, but not gonna write it

So, if I've understood it right, setting u to null in the 2-nd function shouldn't mess up with the 1-st function's functionality, right?

And another question:
is there such thing as "local ability"? or do I need to set an integer to the value of the ability?
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
I can give you a full 5 page explanation of why it works but you can find it yourself.
Just search for Pointers.

In short:
A variable is not a value. It is a pointer to a value.
You null the pointer, the value still exists.

Variables passed as parameters do not necessarily have to be nulled.
Only variables that are agents with exception of players have to be nulled.
(In your first function, only "t", in your second function only "m_targ".
 
Level 12
Joined
Jan 2, 2016
Messages
973
JASS:
call ForGroupBJ( udg_MultiGroup[0], function MS_Arrow )
This works,
JASS:
call ForGroupBJ( udg_MultiGroup[0], MS_Arrow(dummy, m_ut_pt) )
This doesn't
But my function is:
JASS:
function MS_Arrow takes unit d, location ul returns nothing
Is there any way (besides a hashtable, or global variables) to take the values from the previous function?
 
Level 12
Joined
May 22, 2015
Messages
1,051
There is no variable type for ability. However, all functions that work with abilities use integers. The appropriate integers are the ability IDs (same as the unit type IDs I was telling you about in another thread - you view them in the object editor by pressing ctrl+d).

JASS:
function abilityStuff takes nothing returns nothing
    local integer ability = 'A001'
    call UnitAddAbility(udg_myUnit, ability)
endfunction
^This will add the ability with ID 'A001' to udg_myUnit.

EDIT:
Nope for your post above. However, there is a preferred way to do loops over units that also allow you to use your locals in the loop. It is called a FirstOfGroup loop. This tutorial was very helpful for me:
http://www.hiveworkshop.com/forums/...on-using-first-group-loop-enumeration-223140/

Just note that this method is destructive. That means it will change the data in your group and so it will not be reusable (without further workarounds which are described in the tutorial). It empties the group you use each time you do a loop like this. I can help explain it more if the tutorial is not enough.
 
Level 12
Joined
May 22, 2015
Messages
1,051
You only have to look at the functions where they do the looping. The important part for you is:
JASS:
    local unit FoG=null //We initially declare an empty unit handler
    call GroupEnumUnitsInRange(grp,0.,0.,256.,null) //Note that we're using 'null' for our filterfunc. This means that *all* units in range will be added to the group.
    loop
        set FoG=FirstOfGroup(grp) //If you're new to this kind of loop, this part might look strange to you.
        exitwhen FoG==null //combined with the above line, this is effectively checking if the group is empty
        if GetUnitTypeId(FoG)=='hfoo' then //here we have a make-shift condition statement.. followed by an action below
            call KillUnit(FoG)
        endif
        call GroupRemoveUnit(grp,FoG) //here's how we make the loop eventually terminate. Remove first of group, and when the loop restarts we get the new "first of group".
    endloop

Basically, you use this function called FirstOfGroup() which will grab an arbitrary unit from the group (there's no way you can safely determine which one it picks first, but it doesn't matter).

If it gets null, that means it didn't find a unit (there are some edge cases where this is a bug you have to deal with, but we'll ignore that for now). Anyway, you exit the loop if you found null for FirstOfGroup(). This is how you know when to exit the loop.

Then you do your normal loop actions. As an example, the tutorial checks if it's a footman and then kills it if it is.

At the bottom of the loop, you must remove the unit from the group. This is so that you don't infinitely loop on the same unit. However, as I mentioned in my post, it will make the group empty by the end.

What's important is that you don't need to create and destroy the group each time. You create a global group variable and you use that for these loops.

Anyway, since this whole loop is inside one function, you can use local variables inside it. It is also faster for various reasons than ForGroup(...).
 
Level 12
Joined
May 22, 2015
Messages
1,051
I see, thanks :)
But I'm kind a wondering.. is there something like "local unit group"? Can I use a local group for this loop (if such thing exsists)?

It is called just "group".
local group g

However, the problem is you will have to create a group to use it, and if you create one, you will have to destroy it. The only way to use a local here is to do something like set g = udg_myGlobalGroup, but then there is no difference.

Using a global makes it more efficient.
 
Level 12
Joined
May 22, 2015
Messages
1,051
GroupEnumUnitsInRange(grp,0.,0.,256.,null)
what are the 0's for by the way?

Referring to the handy page here:
http://wiki.thehelper.net/wc3/jass/common.j

native GroupEnumUnitsInRange takes group whichGroup, real x, real y, real radius, boolexpr filter returns nothing

You can look all of this up :) (just do ctrl+f to search for your function name). The 0's are the X and Y coordinates. This function gets all the units in range of a point (though without actually using a location which makes it more efficient).
 
Level 12
Joined
May 22, 2015
Messages
1,051
ugh..
Is it normal for world editor to crash when I use this function?
And well, which point does it choose if I don't set a x and y?

Well you have to pass it something. If you give it 0, 0, it will select from the point 0, 0 on your map. This is usually the exact middle of your map, but it can be off if you change your map size after the initial creation (as is the cast on my map).

It usually will not crash. However, I have a hunch. If you are passing a null group (possibly an undefined local group), it will probably crash. This function does not create a new group for you. It empties the group you pass it, and then it adds the units that are within the specified range of the specified point.

If you are giving it an undefined group, just add set g = CreateGroup() before you call that function. However, note that you will have to delete the group, which lowers the efficiency. For simplicity, though, you could just use it like that for now if you want.
 
Level 12
Joined
May 22, 2015
Messages
1,051
I was using a global group in the 1-st place. But it's good to know that I need to set the x and y to something different than 0.
But say.. is it better to get x and y of a point or x and y of a unit?

X and Y of a unit is better. You will never have to destroy units because they clean themselves up when they die. They are a part of your map, so they will be around no matter what. Using a point is okay, however, your map does not need the point object to run. It is basically not part of your map (at least in terms of what a naive player will see). You will have to destroy the point after using it. This is unnecessary processing.

The one case it is fine / probably good is if it is a static point that you define at the start of the map and reuse throughout your map. My map is a hero defense, so I put the waypoints for enemies in point variables at map initialisation. I never destroy them and reuse them in all the relevant triggers.

Using the unit coordinates is probably always better for the cases where the points are not static.

Anyway, are you still having crash problems? It shouldn't crash if you give it x=0 and y=0. At worst, it will just get the wrong units or no units at all. If you are having troubles, I recommend posting your code. It could be that you failed to remove the unit from the group or the exitwhen statement is incorrect.
 
Level 12
Joined
Jan 2, 2016
Messages
973
Yeah, I will post the code once I'm done with it.
I'm remaking my Multishot to use local variables, but the code for it is quite long.
I already need to set a location for it, so I guess using the location's x and y is better for me.

Now I'm wondering about something else. How do I make it select fixed amount of units, using this function?:
JASS:
    local unit m_targ=null
    call GroupEnumUnitsInRange(udg_MultiGroup[0],x,y,256,null)
    loop
        set m_targ=FirstOfGroup(udg_MultiGroup[0])
        set count = (count + 1)
        exitwhen // count >= 10 or m_targ = null
        if // my conditions
            // my actions
        endif
        call GroupRemoveUnit(udg_MultiGroup[0],m_targ)
    endloop

Right now I'm thinking to make few extra functions to remove units form MultiGroup, and to leave as many as I need, but there ought to be a better way.
 
Level 12
Joined
May 22, 2015
Messages
1,051
Does it work if you uncomment that exitwhen part?

Also I think it should be m_targ == null (== instead of =).

I am not certain if this will create a perfectly random set of targets though. FirstOfGroup is arbitrary, but it might be bad sometimes. I don't know how it chooses the order of the units.
 
Level 12
Joined
Jan 2, 2016
Messages
973
Okay... The script doesn't give me any syntax errors, but the multishot doesn't work.
JASS:
function MS_Damage takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local real damage = LoadReal(udg_Table, GetHandleId(t), StringHash("Damage"))
    local unit target = LoadUnitHandle(udg_Table, GetHandleId(t), StringHash("Target"))
    local unit dummy = LoadUnitHandle(udg_Table, GetHandleId(t), StringHash("Dummy"))
    local integer int = LoadInteger(udg_Table, GetHandleId(t), StringHash("AttackType"))
    call UnitDamageTargetBJ( dummy, target, damage, ConvertAttackType(int), DAMAGE_TYPE_NORMAL )
    call FlushChildHashtable(udg_Table, GetHandleId(t))
    call DestroyTimer(t)
    set t = null
    set target = null
    set dummy = null
endfunction

function MS_Values takes real tim, real dam, unit targ, unit dum, integer att returns nothing
    local timer t = CreateTimer()
    call SaveReal(udg_Table, GetHandleId(t), StringHash("Damage"), dam)
    call SaveUnitHandle(udg_Table, GetHandleId(t), StringHash("Target"), targ)
    call SaveUnitHandle(udg_Table, GetHandleId(t), StringHash("Dummy"), dum)
    call SaveInteger(udg_Table, GetHandleId(t), StringHash("AttackType"), att)
    call TimerStart(t, tim , false, function MS_Damage)
    set t = null
endfunction

function DamageBlock takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local real health = LoadReal(udg_Table, GetHandleId(t), StringHash("Health"))
    local unit u = LoadUnitHandle(udg_Table, GetHandleId(t), StringHash("Unit"))
    call UnitRemoveAbilityBJ( 'A02P', u )
    call SetUnitLifeBJ( u, health )
    call FlushChildHashtable(udg_Table, GetHandleId(t))
    call DestroyTimer(t)
    set t = null
    set u = null
endfunction

function DB_Timer takes unit u, real l returns nothing
    local timer t = CreateTimer()
    call SaveUnitHandle(udg_Table, GetHandleId(t), StringHash("Unit"), u)
    call SaveReal(udg_Table, GetHandleId(t), StringHash("Health"), l)
    call TimerStart(t, 0.00, false, function DamageBlock)
    set t = null
    set u = null
endfunction

function ImmunityPart takes unit u, unit d, location ul, location tl, real dam, integer att returns nothing
    local integer a
    local real r
    if (  GetUnitAbilityLevelSwapped('Amim', u ) > 0  ) then
        call UnitRemoveAbilityBJ( 'Amim', u )
        set a = 'Amim'
    else
        if ( GetUnitAbilityLevelSwapped('ACm2', u ) > 0 ) then
            call UnitRemoveAbilityBJ( 'ACm2', u )
            set a = 'ACm2'
        else
            if ( GetUnitAbilityLevelSwapped('ACm3', u ) > 0 ) then
                call UnitRemoveAbilityBJ( 'ACm3', u )
                set a = 'ACm3'
            else
                if ( GetUnitAbilityLevelSwapped('ACmi', u ) > 0 ) then
                    call UnitRemoveAbilityBJ( 'ACmi', u )
                    set a = 'ACmi'
                else
                    set a = udg_null_ability
                endif
            endif
        endif
    endif
    call IssueTargetOrderBJ( d, "acidbomb", u )
    if ( a != udg_null_ability ) then
        set r = ( DistanceBetweenPoints(ul, tl) / 900.00 )
        call MS_Values(r,dam,u,d,att)
    else
    endif
    call UnitAddAbilityBJ( a , u )
endfunction

function MS_Group_3 takes nothing returns nothing
    local unit t = GetEnumUnit()
    call GroupAddUnitSimple( t, udg_MultiGroup[3] )
    set udg_MultishotCount = ( udg_MultishotCount + 1 )
    set t = null
endfunction

function Owner_check takes nothing returns boolean
    return ( IsPlayerEnemy(GetOwningPlayer(GetFilterUnit()), GetOwningPlayer(udg_GDD_DamageSource)) == true )
endfunction

function Multishot takes nothing returns nothing
    local integer count
    local real t_life
    local unit dummy
    local location para_pt
    local real damage
    local unit m_ut = udg_GDD_DamageSource
    local location m_ut_pt = GetUnitLoc(m_ut)
    local real x = GetLocationX(m_ut_pt)
    local real y = GetLocationY(m_ut_pt)
    local unit m_targ = udg_GDD_DamagedUnit
    local location m_point = GetUnitLoc(m_targ)
    local boolean bull = LoadBoolean(udg_Table, GetUnitTypeId(m_ut), StringHash("Upgradeable"))
    local integer targ_am = LoadInteger(udg_Table, GetUnitTypeId(m_ut), StringHash("Targets"))
    local real dist = LoadReal(udg_Table, GetUnitTypeId(m_ut), StringHash("Parabola"))
    local real rang = LoadReal(udg_Table, GetUnitTypeId(m_ut), StringHash("Range"))
    local integer a_type = LoadInteger(udg_Table, GetUnitTypeId(m_ut), StringHash("AttackType"))
    if ( bull == true ) then
        set rang = ( ( 200.00 * I2R(GetPlayerTechCountSimple('R00K', GetOwningPlayer(m_ut))) ) + rang )
    endif
    set para_pt = PolarProjectionBJ(m_ut_pt, dist, AngleBetweenPoints(m_ut_pt, m_point))
    set t_life = GetUnitStateSwap(UNIT_STATE_LIFE, m_targ)
    call CreateNUnitsAtLoc( 1, 'h00C', GetOwningPlayer(m_ut), m_ut_pt, bj_UNIT_FACING )
    set dummy = GetLastCreatedUnit()
    call UnitApplyTimedLifeBJ( 2.00, 'BTLF', dummy )
    call UnitAddAbilityBJ( 'A00P', dummy )
    call UnitAddAbilityBJ( 'A02P', m_targ )
    call SetUnitLifePercentBJ( m_targ, 100 )
    call UnitDamageTargetBJ( dummy , m_targ , 25000.00, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL )
    set damage = ( ( 25000.00 / ( GetUnitStateSwap(UNIT_STATE_MAX_LIFE, m_targ) - GetUnitStateSwap(UNIT_STATE_LIFE, m_targ) ) ) * udg_GDD_Damage )
    call SetUnitLifePercentBJ( m_targ, 100 )
    call SaveReal( udg_Table, GetHandleId(dummy),StringHash("Damage"), damage )
    call SaveInteger( udg_Table , GetHandleId(dummy), StringHash("AttackType"), a_type )
    call DB_Timer(m_targ, t_life)
    call ImmunityPart(m_targ, dummy, m_ut_pt, m_point, damage, a_type)
    call RemoveLocation(m_point)
    set m_targ = null
    if ( IsPlayerAlly(GetOwningPlayer(m_ut), GetOwningPlayer(m_targ)) == true ) then
    else
        set udg_MultiGroup[0] = GetUnitsInRangeOfLocMatching(rang, m_ut_pt , Condition( function Owner_check))
        call GroupRemoveUnitSimple( m_targ , udg_MultiGroup[0] )
        if ( targ_am == 0 ) then
            call GroupEnumUnitsInRange(udg_MultiGroup[0],x,y,rang,null)
            loop
                set m_targ = FirstOfGroup(udg_MultiGroup[0])
                exitwhen m_targ == null
                set m_point = GetUnitLoc(m_targ)
                if ( DistanceBetweenPoints(para_pt, udg_MultiPt) <= ( ( 2.00 * ( dist * RSignBJ(dist) ) ) / ( 1 - CosBJ(( AngleBetweenPoints(para_pt, m_point) - AngleBetweenPoints(m_ut_pt, para_pt) )) ) ) ) then
                    call ImmunityPart( m_targ , dummy , m_ut_pt, m_point, damage, a_type)
                endif
                call GroupRemoveUnitSimple( m_targ , udg_MultiGroup[0])
                call RemoveLocation(m_point)
            endloop    
        else
            set count = 1
            if ( targ_am == 1 ) then
            else
                set m_targ = null
                call RemoveLocation(m_point)
                call GroupEnumUnitsInRange(udg_MultiGroup[0],x,y,rang,null)
                loop
                    set m_targ = FirstOfGroup(udg_MultiGroup[0])
                    exitwhen m_targ == null
                    set m_point = GetUnitLoc(m_targ)
                    if ( DistanceBetweenPoints(para_pt, udg_MultiPt) <= ( ( 2.00 * ( dist * RSignBJ(dist) ) ) / ( 1 - CosBJ(( AngleBetweenPoints(para_pt, m_point) - AngleBetweenPoints(m_ut_pt, para_pt) )) ) ) ) then
                        if (( AngleBetweenPoints(udg_MultiUtPt, udg_MultiPt) - AngleBetweenPoints(udg_MultiUtPt, udg_MultiPoint) ) >= 0.00 ) then
                            call GroupAddUnitSimple( GetEnumUnit(), udg_MultiGroup[1] )
                        else
                            call GroupAddUnitSimple( GetEnumUnit(), udg_MultiGroup[2] )
                        endif
                    endif
                    call GroupRemoveUnitSimple( m_targ , udg_MultiGroup[0])
                    call RemoveLocation(m_point)
                endloop
                if ( CountUnitsInGroup(udg_MultiGroup[2]) >= CountUnitsInGroup(udg_MultiGroup[1]) ) then
                    call ForGroupBJ( GetRandomSubGroup(( ( udg_MultishotTargets - 1 ) / 2 ), udg_MultiGroup[1]), function MS_Group_3 )
                    call ForGroupBJ( GetRandomSubGroup(( udg_MultishotTargets - udg_MultishotCount ), udg_MultiGroup[2]), function MS_Group_3 )
                else
                    call ForGroupBJ( GetRandomSubGroup(( ( udg_MultishotTargets - 1 ) / 2 ), udg_MultiGroup[2]), function MS_Group_3 )
                    call ForGroupBJ( GetRandomSubGroup(( udg_MultishotTargets - udg_MultishotCount ), udg_MultiGroup[1]), function MS_Group_3 )
                endif
                call GroupClear( udg_MultiGroup[1] )
                call GroupClear( udg_MultiGroup[2] )
                call GroupEnumUnitsInRange(udg_MultiGroup[3],x,y,rang,null)
                loop
                    set m_targ = FirstOfGroup(udg_MultiGroup[3])
                    exitwhen m_targ == null
                    set m_point = GetUnitLoc(m_targ)
                    call ImmunityPart( m_targ , dummy , m_ut_pt, m_point, damage, a_type)
                    call RemoveLocation(m_point)
                    call GroupRemoveUnitSimple( m_targ , udg_MultiGroup[3])
                endloop
            endif
        endif
        call GroupClear( udg_MultiGroup[0] )
    endif
    call RemoveLocation(m_ut_pt)
    call RemoveLocation(para_pt)
endfunction

function Trig_Multishot_Conditions takes nothing returns boolean
    if ( not ( GetUnitAbilityLevelSwapped('A00L', udg_GDD_DamageSource) > 0 ) ) then
        call Multishot()
        return false
    endif
    return true
endfunction

//===========================================================================
function InitTrig_Multishot takes nothing returns nothing
    set gg_trg_Multishot = CreateTrigger(  )
    call TriggerRegisterVariableEvent( gg_trg_Multishot, "udg_GDD_Event", EQUAL, 0 )
    call TriggerAddCondition( gg_trg_Multishot, Condition( function Trig_Multishot_Conditions ) )
endfunction

Here is the version, that's using global variables, and it works:
JASS:
function MS_Damage takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local real damage = LoadReal(udg_Table, GetHandleId(t), StringHash("Damage"))
    local unit target = LoadUnitHandle(udg_Table, GetHandleId(t), StringHash("Target"))
    local unit dummy = LoadUnitHandle(udg_Table, GetHandleId(t), StringHash("Dummy"))
    local integer int = LoadInteger(udg_Table, GetHandleId(t), StringHash("AttackType"))
    call UnitDamageTargetBJ( dummy, target, damage, ConvertAttackType(int), DAMAGE_TYPE_NORMAL )
    call FlushChildHashtable(udg_Table, GetHandleId(t))
    call DestroyTimer(t)
    set t = null
    set target = null
    set dummy = null
endfunction

function MS_Values takes nothing returns nothing
    local timer t = CreateTimer()
    call SaveReal(udg_Table, GetHandleId(t), StringHash("Damage"), udg_Temp_Real)
    call SaveUnitHandle(udg_Table, GetHandleId(t), StringHash("Target"), udg_MultiUnit)
    call SaveUnitHandle(udg_Table, GetHandleId(t), StringHash("Dummy"), udg_Temp_Unit)
    call SaveInteger(udg_Table, GetHandleId(t), StringHash("AttackType"), udg_Temp_Integer)
    call TimerStart(t, udg_TempReal, false, function MS_Damage)
    set t = null
endfunction

function DamageBlock takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local real health = LoadReal(udg_Table, GetHandleId(t), StringHash("Health"))
    local unit u = LoadUnitHandle(udg_Table, GetHandleId(t), StringHash("Unit"))
    call UnitRemoveAbilityBJ( 'A02P', u )
    call SetUnitLifeBJ( u, health )
    call FlushChildHashtable(udg_Table, GetHandleId(t))
    call DestroyTimer(t)
    set t = null
    set u = null
endfunction

function DB_Timer takes nothing returns nothing
    local timer t = CreateTimer()
    call SaveUnitHandle(udg_Table, GetHandleId(t), StringHash("Unit"), udg_MultiTarget)
    call SaveReal(udg_Table, GetHandleId(t), StringHash("Health"), udg_Temp_Life)
    call TimerStart(t, 0.00, false, function DamageBlock)
    set t = null
endfunction

function ImmunityPart takes nothing returns nothing
    if (  GetUnitAbilityLevelSwapped('Amim', udg_MultiUnit) > 0  ) then
        call UnitRemoveAbilityBJ( 'Amim', udg_MultiUnit )
        set udg_Immunity = 'Amim'
    else
        if ( GetUnitAbilityLevelSwapped('ACm2', udg_MultiUnit) > 0 ) then
            call UnitRemoveAbilityBJ( 'ACm2', udg_MultiUnit )
            set udg_Immunity = 'ACm2'
        else
            if ( GetUnitAbilityLevelSwapped('ACm3', udg_MultiUnit) > 0 ) then
                call UnitRemoveAbilityBJ( 'ACm3', udg_MultiUnit )
                set udg_Immunity = 'ACm3'
            else
                if ( GetUnitAbilityLevelSwapped('ACmi', udg_MultiUnit) > 0 ) then
                    call UnitRemoveAbilityBJ( 'ACmi', udg_MultiUnit )
                    set udg_Immunity = 'ACmi'
                else
                    set udg_Immunity = udg_null_ability
                endif
            endif
        endif
    endif
    call IssueTargetOrderBJ( udg_Temp_Unit, "acidbomb", udg_MultiUnit )
    if ( udg_Immunity != udg_null_ability ) then
        set udg_TempReal = ( DistanceBetweenPoints(udg_MultiUtPt, udg_MultiPt) / 900.00 )
        call MS_Values()
    else
    endif
    call UnitAddAbilityBJ( udg_Immunity, udg_MultiUnit )
endfunction

function MS_Arrow takes nothing returns nothing
    set udg_MultiUnit = GetEnumUnit()
    set udg_MultiPt = GetUnitLoc(udg_MultiUnit)
    if ( DistanceBetweenPoints(udg_MultiParaPt, udg_MultiPt) <= ( ( 2.00 * ( udg_MultishotAngle * RSignBJ(udg_MultishotAngle) ) ) / ( 1 - CosBJ(( AngleBetweenPoints(udg_MultiParaPt, udg_MultiPt) - AngleBetweenPoints(udg_MultiUtPt, udg_MultiParaPt) )) ) ) ) then
        call ImmunityPart()
    else
    endif
    call RemoveLocation (udg_MultiPt)
endfunction

function IF_MSC_L takes nothing returns nothing
    set udg_MultiPt = GetUnitLoc(GetEnumUnit())
    if ( DistanceBetweenPoints(udg_MultiParaPt, udg_MultiPt) <= ( ( 2.00 * ( udg_MultishotAngle * RSignBJ(udg_MultishotAngle) ) ) / ( 1 - CosBJ(( AngleBetweenPoints(udg_MultiParaPt, udg_MultiPt) - AngleBetweenPoints(udg_MultiUtPt, udg_MultiParaPt) )) ) ) ) then
        if ( ( AngleBetweenPoints(udg_MultiUtPt, udg_MultiPt) - AngleBetweenPoints(udg_MultiUtPt, udg_MultiPoint) ) >= 0.00 ) then
            call GroupAddUnitSimple( GetEnumUnit(), udg_MultiGroup[1] )
        else
            call GroupAddUnitSimple( GetEnumUnit(), udg_MultiGroup[2] )
        endif
    else
    endif
    call RemoveLocation (udg_MultiPt)
endfunction

function MS_Arrow_L takes nothing returns nothing
    set udg_MultiUnit = GetEnumUnit()
    call ImmunityPart()
    set udg_MultishotCount = ( udg_MultishotCount + 1 )
endfunction

function Owner_check takes nothing returns boolean
    return ( IsPlayerEnemy(GetOwningPlayer(GetFilterUnit()), GetOwningPlayer(udg_GDD_DamageSource)) == true )
endfunction

function Multishot takes nothing returns nothing
    set udg_MultiUtPt = GetUnitLoc(udg_GDD_DamageSource)
    set udg_MultiTarget = udg_GDD_DamagedUnit
    set udg_MultiUnit = udg_MultiTarget
    set udg_MultiPoint = GetUnitLoc(udg_MultiTarget)
    set udg_MultiPt = udg_MultiPoint
    set udg_Temp_Boolean = LoadBoolean(udg_Table, GetUnitTypeId(udg_GDD_DamageSource), StringHash("Upgradeable"))
    set udg_MultishotTargets = LoadInteger(udg_Table, GetUnitTypeId(udg_GDD_DamageSource), StringHash("Targets"))
    set udg_MultishotAngle = LoadReal(udg_Table, GetUnitTypeId(udg_GDD_DamageSource), StringHash("Parabola"))
    set udg_MultishotRange = LoadReal(udg_Table, GetUnitTypeId(udg_GDD_DamageSource), StringHash("Range"))
    set udg_Temp_Integer = LoadInteger(udg_Table, GetUnitTypeId(udg_GDD_DamageSource), StringHash("AttackType"))
    if ( udg_Temp_Boolean == true ) then
        set udg_MultishotRange = ( ( 200.00 * I2R(GetPlayerTechCountSimple('R00K', GetOwningPlayer(udg_GDD_DamageSource))) ) + udg_MultishotRange )
    endif
    set udg_MultiParaPt = PolarProjectionBJ(udg_MultiUtPt, udg_MultishotAngle, AngleBetweenPoints(udg_MultiUtPt, udg_MultiPoint))
    set udg_Temp_Life = GetUnitStateSwap(UNIT_STATE_LIFE, udg_MultiTarget)
    call CreateNUnitsAtLoc( 1, 'h00C', GetOwningPlayer(udg_GDD_DamageSource), udg_MultiUtPt, bj_UNIT_FACING )
    call UnitApplyTimedLifeBJ( 2.00, 'BTLF', GetLastCreatedUnit() )
    set udg_Temp_Unit = GetLastCreatedUnit()
    call UnitAddAbilityBJ( 'A00P', udg_Temp_Unit )
    call UnitAddAbilityBJ( 'A02P', udg_MultiTarget )
    call SetUnitLifePercentBJ( udg_MultiTarget, 100 )
    call UnitDamageTargetBJ( GetLastCreatedUnit(), udg_MultiTarget, 25000.00, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL )
    set udg_Temp_Real = ( ( 25000.00 / ( GetUnitStateSwap(UNIT_STATE_MAX_LIFE, udg_MultiTarget) - GetUnitStateSwap(UNIT_STATE_LIFE, udg_MultiTarget) ) ) * udg_GDD_Damage )
    call SetUnitLifePercentBJ( udg_MultiTarget, 100 )
    call SaveRealBJ( udg_Temp_Real, StringHashBJ("Damage"), GetHandleIdBJ(udg_Temp_Unit), udg_Table )
    call SaveIntegerBJ( udg_Temp_Integer, StringHashBJ("AttackType"), GetHandleIdBJ(udg_Temp_Unit), udg_Table )
    call DB_Timer()
    call ImmunityPart()
    if ( IsPlayerAlly(GetOwningPlayer(udg_GDD_DamageSource), GetOwningPlayer(udg_MultiTarget)) == true ) then
    else
        set udg_MultiGroup[0] = GetUnitsInRangeOfLocMatching(udg_MultishotRange, udg_MultiUtPt, Condition( function Owner_check))
        call GroupRemoveUnitSimple( udg_MultiTarget, udg_MultiGroup[0] )
        if ( udg_MultishotTargets == 0 ) then
            call ForGroupBJ( udg_MultiGroup[0], function MS_Arrow )
        else
            set udg_MultishotCount = 1
            if ( udg_MultishotTargets == 1 ) then
            else
                call ForGroupBJ( udg_MultiGroup[0], function IF_MSC_L )
                if ( CountUnitsInGroup(udg_MultiGroup[2]) >= CountUnitsInGroup(udg_MultiGroup[1]) ) then
                    call ForGroupBJ( GetRandomSubGroup(( ( udg_MultishotTargets - 1 ) / 2 ), udg_MultiGroup[1]), function MS_Arrow_L )
                    call ForGroupBJ( GetRandomSubGroup(( udg_MultishotTargets - udg_MultishotCount ), udg_MultiGroup[2]), function MS_Arrow_L )
                else
                    call ForGroupBJ( GetRandomSubGroup(( ( udg_MultishotTargets - 1 ) / 2 ), udg_MultiGroup[2]), function MS_Arrow_L )
                    call ForGroupBJ( GetRandomSubGroup(( udg_MultishotTargets - udg_MultishotCount ), udg_MultiGroup[1]), function MS_Arrow_L )
                endif
                call GroupClear( udg_MultiGroup[1] )
                call GroupClear( udg_MultiGroup[2] )
            endif
        endif
        call GroupClear( udg_MultiGroup[0] )
    endif
    call RemoveLocation(udg_MultiUtPt)
    call RemoveLocation(udg_MultiParaPt)
endfunction

function Trig_Multishot_Conditions takes nothing returns boolean
    if ( not ( GetUnitAbilityLevelSwapped('A00L', udg_GDD_DamageSource) > 0 ) ) then
        return false
    endif
    return true
endfunction

//===========================================================================
function InitTrig_Multishot_Backup takes nothing returns nothing
    set gg_trg_Multishot_Backup = CreateTrigger(  )
    call TriggerRegisterVariableEvent( gg_trg_Multishot_Backup, "udg_GDD_Event", EQUAL, 0 )
    call TriggerAddCondition( gg_trg_Multishot_Backup, Condition( function Trig_Multishot_Conditions ) )
    call TriggerAddAction( gg_trg_Multishot_Backup, function Multishot )
endfunction
Sorry for the long scripts xP

I will add some "display text" actions to see which part doesn't work.
 
Level 12
Joined
Jan 2, 2016
Messages
973
Aha, found what wasn't working: I tried to add it as "condition"
I added the original condition as condition, but I also added a function call (calling my main trigger), via the condition function. It didn't work that way, but it seems to work, when it's in the "Action" part of the trigger.
JASS:
function Trig_Multishot_Conditions takes nothing returns boolean
    if ( not ( GetUnitAbilityLevelSwapped('A00L', udg_GDD_DamageSource) > 0 ) ) then
        call Multishot()
        return false
    endif
    return true
endfunction
This was the wrong part.

EDIT: now that I pointed it out, I see that I had put it in the "false" part of the condition.... Perhaps that was the real error.

EDIT 2: now I am testing if it's working properly and it doesn't xP Main target gets hit 2-ce, and when the max targets allowed are limited - it hits EVERYTHING (including allies, and the shooter herself)
 
Level 12
Joined
May 22, 2015
Messages
1,051
Based on what I see, it seems like the ability ID is wrong or that it will only work on units without the multishot ability.

When a condition returns false, that tells the trigger that the conditions failed. This causes the trigger to do nothing else. When it returns true, that means the conditions passed. The trigger then runs its actions.

By calling the Multishot() function inside the part where it will return false, that means your conditions were not met. That means:
not ( GetUnitAbilityLevelSwapped('A00L', udg_GDD_DamageSource) > 0 )
is true, which is the same as:
GetUnitAbilityLevelSwapped('A00L', udg_GDD_DamageSource) <= 0
which is basically saying the unit must not have the ability 'A00L'
 
Level 12
Joined
Jan 2, 2016
Messages
973
Another question:
Is there a way to get
JASS:
set g = GetUnitsInRangeOfLocMatching(300, l, Condition( function owner_check ))
done without writing owner_check in a seperate function?
I tried several things, but none of them worked...
I don't want to use global variables in my JASS triggers, but this thing is forcing me to....
 
Level 12
Joined
Jan 2, 2016
Messages
973
To be honest this makes it even harder.
I mostly need this "matching" when I'm going to select a sub-group later, so I don't want any allied units in the group to reduce the max count without an effect.
I could make another group and transfer the enemy units to it and then do the actions to that group, or do it with the "GetFirstOfGroup" and increase the counter's value only if the picked unit is enemy, but as I said - it's easier to leave it as it is and keep using a global variable.

Are you sure there is no way to make it work the way I want? xP
 
Level 12
Joined
May 22, 2015
Messages
1,051
To be honest this makes it even harder.
I mostly need this "matching" when I'm going to select a sub-group later, so I don't want any allied units in the group to reduce the max count without an effect.
I could make another group and transfer the enemy units to it and then do the actions to that group, or do it with the "GetFirstOfGroup" and increase the counter's value only if the picked unit is enemy, but as I said - it's easier to leave it as it is and keep using a global variable.

Are you sure there is no way to make it work the way I want? xP

His suggestion is to use a FoG loop, basically. It is the standard way to do unit group loops since it is most efficient, easy to follow (no unnecessary jumps to other functions), and allows you to use local variables.

Also note that GroupEnumUnitsInRange() is basically the same function but without using a location, so it is also usually faster. It also doesn't create a new group (you have to pass it one), so you don't have to delete it afterwards.
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Are you sure there is no way to make it work the way I want? xP
Is there no way you want the way it works?

There is literally no difference between a filter and a ForGroup()... kinda.
If you want to do a good search but maintain the group, you can still use FirstOfGroup iteration by either switching to another group or using a swap group to keep the original handle of the group the same.
 
Level 12
Joined
Jan 2, 2016
Messages
973
EDIT: (Deleted old post. Figured what was wrong with it)
When I have a unit group array, with size set to "1" - I can't add units to group[1], [2] and above.
I need to define the Unit group [1+] before that.
How do I define an empty unit group and "add" units later?

EDIT 2: apparently "set udg_Group_FrostDomain[udg_Count_FrostDomain] = GetUnitsInRangeOfLocAll(0, cast_pt)"
does the job, but umm... does it add the triggering unit (cast_pt = location of triggering unit) to the group?
 
Last edited:
Level 12
Joined
May 22, 2015
Messages
1,051
EDIT: (Deleted old post. Figured what was wrong with it)
When I have a unit group array, with size set to "1" - I can't add units to group[1], [2] and above.
I need to define the Unit group [1+] before that.
How do I define an empty unit group and "add" units later?

EDIT 2: apparently "set udg_Group_FrostDomain[udg_Count_FrostDomain] = GetUnitsInRangeOfLocAll(0, cast_pt)"
does the job, but umm... does it add the triggering unit (cast_pt = location of triggering unit) to the group?

Just use the native constructor:
set udg_Group_FrostDomain[1] = CreateGroup()
 
Level 12
Joined
Jan 2, 2016
Messages
973
Okay, something is just WRONG here.
JASS:
    call DisplayTextToForce(GetPlayersAll(), GetUnitName(shooter))
    if (al_hits_main_tar == true) then
        call Shoot(m_targ, dam, dummy, a_type, m_ut_pt, m_point)
        call DisplayTextToForce(GetPlayersAll(), "true")
    endif
    call DisplayTextToForce(GetPlayersAll(), GetUnitName(shooter))
I see the name of the shooter only once, and then nothing....
What is going on?
Here is the "Shoot" function, if it helps:
JASS:
function Shoot takes unit target, real damage, unit dummy, integer a_type, location sh_pt, location tar_pt returns nothing
    local integer immunity
    local real delay
    set immunity = ImmunityPart(target, 'Amim', immunity)
    set immunity = ImmunityPart(target, 'ACm2', immunity)
    set immunity = ImmunityPart(target, 'ACm3', immunity)
    set immunity = ImmunityPart(target, 'ACmi', immunity)
    call IssueTargetOrder(dummy, "acidbomb", target)
    call DisplayTextToForce(GetPlayersAll(), R2S(damage))
    if immunity != 0 then
        set delay = DistanceBetweenPoints(sh_pt, tar_pt)/900.00
        call FakeDamage(target, damage, dummy, delay, a_type)
        call UnitAddAbility(target, immunity)
    endif
endfunction

It was working kind a fine before I seperated the "Shoot" into another function.

EDIT: Okay... turned out that declaring a variable isn't enough.. When I set "immunity" to 0 when declaring it - it started working.
But.. Why was it blocking my main trigger before?
Wasn't only the function suppose to malfunction?
 
Level 12
Joined
Jan 2, 2016
Messages
973
JASS:
function ImmunityPart takes unit u, integer id, integer old_immunity returns integer
    if( GetUnitAbilityLevel(u, id) > 0 ) then
        call UnitRemoveAbility(u,id)
        return id
    endif
    return old_immunity
endfunction
JASS:
function FakeDamage takes unit targ, real dam, unit d_dealer, real delay, integer a_type returns nothing
    local timer t = CreateTimer()
    local integer id = GetHandleId(t)
    call SaveUnitHandle(udg_Table, id, 'targ', targ)
    call SaveUnitHandle(udg_Table, id, 'dumy', d_dealer)
    call SaveReal(udg_Table, id, 'dmge', dam)
    call SaveInteger(udg_Table, id, 'atyp', a_type)
    call TimerStart(t, delay, false, function DamageApply)
    set t = null
    set targ = null
    set d_dealer = null
endfunction
 
Level 12
Joined
Jan 2, 2016
Messages
973
By the way. Few minutes ago I realized why didn't the "GetEnumUnitsInRange" work properly last time I tried it.
I didn't realize it was putting all the units in range into the unit group.
But now, after finding out what it actually does, I have a question.
If I have some unit group (made by some trigger), can I use the "FirstOfGroup" loop, without using the GetEnumUnitsInRange?
Example:
JASS:
    local unit s_targ = null
    loop
        set s_targ = FirstOfGroup(udg_Group1)
        exitwhen s_targ == null
        if( IsPlayerEnemy(GetOwningPlayer(s_targ), GetOwningPlayer(GetTriggerUnit()) == true) then
            // actions
        endif
        call GroupRemoveUnit(udg_Group1, s_targ)
    endloop
 
Status
Not open for further replies.
Top