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

Damages up to three targets

Status
Not open for further replies.
Level 19
Joined
Oct 7, 2014
Messages
2,209
Hello Hive I'm asking if anyone could help with this trigger.
I want to target one enemy unit but also damages the two nearest enemy units with this spell.

  • Ancestral Flame
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Ancestral Flame
    • Actions
      • Set AF_Target = (Target unit of ability being cast)
      • Set AF_Damage[1] = (Real(((Intelligence of (Triggering unit) (Include bonuses)) x 10)))
      • Set AF_Damage[2] = (Real(((Intelligence of (Triggering unit) (Include bonuses)) x 12)))
      • Set AF_Damage[3] = (Real(((Intelligence of (Triggering unit) (Include bonuses)) x 15)))
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Level of Ancestral Flame for (Triggering unit)) Equal to 1
        • Then - Actions
          • Unit - Cause (Triggering unit) to damage AF_Target, dealing AF_Damage[1] damage of attack type Spells and damage type Normal
        • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Level of Ancestral Flame for (Triggering unit)) Equal to 2
        • Then - Actions
          • Unit - Cause (Triggering unit) to damage AF_Target, dealing AF_Damage[2] damage of attack type Spells and damage type Normal
        • Else - Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Level of Ancestral Flame for (Triggering unit)) Equal to 3
        • Then - Actions
          • Unit - Cause (Triggering unit) to damage AF_Target, dealing AF_Damage[3] damage of attack type Spells and damage type Normal
        • Else - Actions
 
Level 19
Joined
Jul 14, 2011
Messages
875
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • (Level of Ancestral Flame for (Triggering unit)) Equal to 1
    • Then - Actions
      • Unit - Cause (Triggering unit) to damage AF_Target, dealing AF_Damage[1] damage of attack type Spells and damage type Normal
    • Else - Actions
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • (Level of Ancestral Flame for (Triggering unit)) Equal to 2
    • Then - Actions
      • Unit - Cause (Triggering unit) to damage AF_Target, dealing AF_Damage[2] damage of attack type Spells and damage type Normal
    • Else - Actions
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • (Level of Ancestral Flame for (Triggering unit)) Equal to 3
    • Then - Actions
      • Unit - Cause (Triggering unit) to damage AF_Target, dealing AF_Damage[3] damage of attack type Spells and damage type Normal
    • Else - Actions
->
  • Unit - Cause (Triggering unit) to damage AF_Target, dealing AF_Damage[Level of Ancestral Flame for (Triggering unit)] damage of attack type Spells and damage type Normal
GetClosestUnit(s)
 
Level 19
Joined
Oct 7, 2014
Messages
2,209
- PickAllUnitsInRange
- Keep track of unit itself and range of 'PickedUnit' towards targeted unit.
- Make certain checks like "real comparisons" to evaluate the smallest distance of the unit group.

After the group enumeration maximum 2 units should be stored into variables, which will be damaged now.
I couldn't understand how to apply it.

  • Unit - Cause (Triggering unit) to damage AF_Target, dealing AF_Damage[Level of Ancestral Flame for (Triggering unit)] damage of attack type Spells and damage type Normal
GetClosestUnit(s)
So I can reduce the trigger to this thanks man.
Sorry but I couldn't understand JASS and I'm still noob at making GUI.
 
Level 19
Joined
Jul 14, 2011
Messages
875
Lets say you have a group variable named G. In JASS global variables get the prefix 'udg_', which makes the group udg_G, for example.

You can then use the following:
  • Custom script: set udg_G = GetClosestUnitsInRange(GetUnitX(udg_AF_Target), GetUnitY(udg_AF_Target), 500, 3, null)
That will return the 3 closest units of AF_Target, including AF_Target itself.

If you want to check for enemies, put this in your map header (for example):
JASS:
function EnemyFilter takes nothing returns boolean
    return IsUnitEnemy(GetFilterUnit(), GetTriggerPlayer())
endfunction

and use this instead of the above custom script:
  • Custom script: set udg_G = GetClosestUnitsInRange(GetUnitX(udg_AF_Target), GetUnitY(udg_AF_Target), 500, 3, Filter(function EnemyFilter))
Then whats left is to pick every unit in 'G' and do your actions.

The '500' is the range it searches for (as I assume your ability wont damage units all over the map).
You should also destroy the group at the end.
 
Level 19
Joined
Oct 7, 2014
Messages
2,209
  • Ancestral Flame
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Ancestral Flame
    • Actions
      • Set AF_Target = (Target unit of ability being cast)
      • Set AF_Damage[1] = (Real(((Intelligence of (Triggering unit) (Include bonuses)) x 10)))
      • Set AF_Damage[2] = (Real(((Intelligence of (Triggering unit) (Include bonuses)) x 12)))
      • Set AF_Damage[3] = (Real(((Intelligence of (Triggering unit) (Include bonuses)) x 15)))
      • Custom script: set udg_G = GetClosestUnitsInRange(GetUnitX(udg_AF_Target), GetUnitY(udg_AF_Target), 500, 3, Filter(function EnemyFilter))
      • Unit Group - Pick every unit in AF_RangeUnit and do (Actions)
        • Loop - Actions
          • Unit - Cause (Triggering unit) to damage (Picked unit), dealing AF_Damage[(Level of Ancestral Flame for (Triggering unit))] damage of attack type Spells and damage type Normal
      • Custom script: call DestroyGroup(udg_AF_RangeUnit)
I keep getting this error in the Custom Script you said that I should wrote it expects a name?

attachment.php
 

Attachments

  • Error.png
    Error.png
    44.2 KB · Views: 108
Level 19
Joined
Oct 7, 2014
Messages
2,209
  • Ancestral Flame
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Ancestral Flame
    • Actions
      • Set AF_Target = (Target unit of ability being cast)
      • Set AF_Damage[1] = (Real(((Intelligence of (Triggering unit) (Include bonuses)) x 10)))
      • Set AF_Damage[2] = (Real(((Intelligence of (Triggering unit) (Include bonuses)) x 12)))
      • Set AF_Damage[3] = (Real(((Intelligence of (Triggering unit) (Include bonuses)) x 15)))
      • Set AF_RangeUnit = (Units within 800.00 of (Position of AF_Target))
      • Unit Group - Pick every unit in AF_RangeUnit and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • ((Picked unit) is A structure) Equal to False
              • ((Picked unit) belongs to an enemy of (Triggering player)) Equal to True
              • (Distance between (Position of AF_Target) and (Position of (Picked unit))) Less than or equal to 300.00
            • Then - Actions
              • Unit - Cause (Triggering unit) to damage (Picked unit), dealing AF_Damage[(Level of Ancestral Flame for (Triggering unit))] damage of attack type Spells and damage type Normal
              • Custom script: call DestroyGroup(udg_AF_RangeUnit)
            • Else - Actions
This is what I have done so far. I've already picked the units in range. Made conditions to check if its a structure, owner of unit is an enemy and Distance between Position of the first targeted unit and Position of picked unit is 300.

But I couldn't figure out how to evalute the 2 nearest unit and store it in a variable.
 
Way to find largest distance for example:

variable currentDistance (real)
variable largestDistance (real)

variable currentUnit (unit)
variable finalTargetUnit (unit)

variable P1 (point)
variable P2 (point)


pseudo code:

Code:
Set finalTargetUnit = NoUnit (To ensure the variable is empty before loop.)
Set P1 = PositionOf(TriggeringUnit)
Set currentDistance = 0
Set largestDistance = 0

loop/enumeration
    Set currentUnit = PickedUnit
    Set P2 = PositionOf(currentUnit)
    Set currentDistance = DistanceBetweenPoints(P1, P2)
    If (finalTargetUnit == NoUnit) or (currentDistance > largestDistance) then
        Set finalTargetUnit = currentUnit
        Set largestDistance = currentDistance
    endif
    Custom script: call RemoveLocation(udg_P2)
endloop
Custom script: call RemoveLocation(udg_P1)

If finalTargetUnit != NoUnit then
    Unit - Damage finalTargetUnit
else
    Print - "No unit was in range."
endif

So you basicly always check the current distance to your backup value. If it's larger you just overwrite your backup variable, else you do just nothing.
In the end you should have compared all distances, so what stays is just the largest one in your back up value.

That's the basic idea here. For your example you need some more checks because you want 3 units.

Edit:

Gismo359 suggested you a system that would simplify it for you. You probably don't have such a filter "EnemyFilter" (?), that why it doesn't like it.
 
Level 19
Joined
Jul 14, 2011
Messages
875
The filter, is actually the function I posted. Since the function requires a boolexpr you must use either ...Condition(function <Name>)... or ...Filter(function <Name>)...(which are exactly the same, I think just used in different cases). Did you copy the function in my previous post to your map header (trigger editor > map name)?
 
If you did it correctly with the filter, you maybe don't have created a group variable "G"?
Because I see you still pick the wrong group then: "Pick every unit in AF_RangeUnit"
So make:
Custom script: set udg_AF_RangeUnit = GetClosestUnitsInRange(GetUnitX(udg_AF_Target), GetUnitY(udg_AF_Target), 500, 3, Filter(function EnemyFilter))
 
Then copy & paste following code into your map header:
JASS:
//==============================================================================
//---DiscipleOfLife's-----------------------------------------------------------

//          GCU // GetClosestUnit(s) v1.3.1 without vJass

//------------------------------------------------------------------------------
//
//                Contents:
//
//  This library contains the following functions, each of which return the
//  unit that is closest to given coordinates and passes a specified filter:
//
//  - GetClosestUnit(x, y, filter)
//  - GetClosestUnitAlt(x, y, filter)
//  - GetClosestUnitInRange(x, y, radius, filter)
//  - GetClosestUnitInGroup(x, y, g)
//
//  Included are also the following functions, each of which return a
//  group consisting of the units closest to given coordinates that
//  pass a specified filter:
//
//  - GetClosestUnits(x, y, n, filter)
//  - GetClosestUnitsInRange(x, y, radius, n, filter)
//  - GetClosestUnitsInGroup(x, y, g, n)
//
//                  Notes:
//
//  - flying heights  and the  height of  the  ground  aren't  taken  into
//    account, only the distance in 2D
//
//  - these functions can't be used in the boolexpr's passed to them
//
//  - all of the functions in this library go through many units  to  find
//    the closest one, meaning that the functions'  performance is  highly
//    dependant on the amount of units they have to go through
//
//    - this without vJass version is as efficient as the vJass version
//
//                Implementing Instructions:
//
//  - either  paste this system  into  your map's  header  or  inside  any
//    trigger  that  has been  converted  to  custom text, that  has  been
//    created before any other triggers  in which you wish  to  use  these
//    functions!
//
//  - press  Ctrl+B to open  the variable  editor and create the following
//    variables leaving their initial values to the default ones
//
//              Type        Name
//              Unit        GCU_CurrentPick
//              Real        GCU_CurrentX
//              Real        GCU_CurrentY
//              Real        GCU_CurrentDistance
//              Unit Group  GCU_AnyGroup
//              Unit Group  GCU_ResultingGroup
//
//       - congrats, you are done!
//
//------------------------------------------------------------------------------
   
    function GCU_Enum takes nothing returns nothing
        local unit u = GetEnumUnit()
        local real dx = GetUnitX(u) - udg_GCU_CurrentX
        local real dy = GetUnitY(u) - udg_GCU_CurrentY
        local real d = (dx*dx + dy*dy) / 10000.
        if d < udg_GCU_CurrentDistance then
            set udg_GCU_CurrentDistance = d
            set udg_GCU_CurrentPick = u
        endif
        set u = null
    endfunction
   
    //==========================================================================
    // Finds the unit that is closest to (x, y) from all units on the map that
    // pass the filter and do not have the locust ability.
    //
    function GetClosestUnit takes real x, real y, boolexpr filter returns unit
        local real r = 800.
        loop
            call GroupEnumUnitsInRange(udg_GCU_AnyGroup, x, y, r, filter)
            exitwhen FirstOfGroup(udg_GCU_AnyGroup) != null
            if r >= 3200. then
                call GroupEnumUnitsInRect(udg_GCU_AnyGroup, bj_mapInitialPlayableArea, filter)
                exitwhen true
            endif
            set r = 2.00 * r
        endloop
        set udg_GCU_CurrentPick = null
        set udg_GCU_CurrentX = x
        set udg_GCU_CurrentY = y
        set udg_GCU_CurrentDistance = 100000
        call ForGroup(udg_GCU_AnyGroup, function GCU_Enum)
        return udg_GCU_CurrentPick
    endfunction
   
    //==========================================================================
    // Does the same as above. Faster when there are no units that pass the
    // filter in a 3200 radius, but at other times slower, and most likely a
    // lot slower. How much faster is somewhat directly proportional to the
    // amount of units that do not pass the filter inside that 3200 radius.
    //
    function GetClosestUnitAlt takes real x, real y, boolexpr filter returns unit
        set udg_GCU_CurrentPick = null
        set udg_GCU_CurrentX = x
        set udg_GCU_CurrentY = y
        set udg_GCU_CurrentDistance = 100000
        call GroupEnumUnitsInRect(udg_GCU_AnyGroup, bj_mapInitialPlayableArea, filter)
        call ForGroup(udg_GCU_AnyGroup, function GCU_Enum)
        return udg_GCU_CurrentPick
    endfunction
   
   
    //==========================================================================
    // Finds the unit that is closest to (x, y) from all units in the specified
    // radius, that pass the filter and do not have the locust ability.
    //
    function GetClosestUnitInRange takes real x, real y, real radius, boolexpr filter returns unit
        set udg_GCU_CurrentPick = null
        set udg_GCU_CurrentX = x
        set udg_GCU_CurrentY = y
        set udg_GCU_CurrentDistance = 100000
        call GroupEnumUnitsInRange(udg_GCU_AnyGroup, x, y, radius, filter)
        call ForGroup(udg_GCU_AnyGroup, function GCU_Enum)
        return udg_GCU_CurrentPick
    endfunction
   
    //==========================================================================
    // Finds the unit that is closest to (x, y) from whichGroup. Unlike the
    // other versions this one considers locusted units also.
    //
    function GetClosestUnitInGroup takes real x, real y, group whichGroup returns unit
        set udg_GCU_CurrentPick = null
        set udg_GCU_CurrentX = x
        set udg_GCU_CurrentY = y
        set udg_GCU_CurrentDistance = 100000
        call ForGroup(whichGroup, function GCU_Enum)
        return udg_GCU_CurrentPick
    endfunction
   
    //==========================================================================
    // The following three functions do the same as the preceding ones, with  //
    // the exception that they return groups consisting of the n closest      //
    // units instead of the closest unit                                      //
    //==========================================================================
   
    function GetClosestUnits takes real x, real y, integer n, boolexpr filter returns group
        call GroupEnumUnitsInRect(udg_GCU_AnyGroup, bj_mapInitialPlayableArea, filter)
        set udg_GCU_ResultingGroup = CreateGroup()
        set udg_GCU_CurrentX = x
        set udg_GCU_CurrentY = y
        loop
            exitwhen n == 0
            set udg_GCU_CurrentPick = null
            set udg_GCU_CurrentDistance = 100000
            call ForGroup(udg_GCU_AnyGroup, function GCU_Enum)
            exitwhen udg_GCU_CurrentPick == null
            call GroupRemoveUnit(udg_GCU_AnyGroup, udg_GCU_CurrentPick)
            call GroupAddUnit(udg_GCU_ResultingGroup, udg_GCU_CurrentPick)
            set n = n - 1
        endloop
        return udg_GCU_ResultingGroup
    endfunction
   
    function GetClosestUnitsInRange takes real x, real y, real radius, integer n, boolexpr filter returns group
        call GroupEnumUnitsInRange(udg_GCU_AnyGroup, x, y, radius, filter)
        set udg_GCU_ResultingGroup = CreateGroup()
        set udg_GCU_CurrentX = x
        set udg_GCU_CurrentY = y
        loop
            exitwhen n == 0
            set udg_GCU_CurrentPick = null
            set udg_GCU_CurrentDistance = 100000
            call ForGroup(udg_GCU_AnyGroup, function GCU_Enum)
            exitwhen udg_GCU_CurrentPick == null
            call GroupRemoveUnit(udg_GCU_AnyGroup, udg_GCU_CurrentPick)
            call GroupAddUnit(udg_GCU_ResultingGroup, udg_GCU_CurrentPick)
            set n = n - 1
        endloop
        return udg_GCU_ResultingGroup
    endfunction
   
    function GCU_AnyGroupAddGroupEnum takes nothing returns nothing
        call GroupAddUnit(udg_GCU_AnyGroup, GetEnumUnit())
    endfunction
   
    function GetClosestUnitsInGroup takes real x, real y, group whichGroup, integer n returns group
        call GroupClear(udg_GCU_AnyGroup)
        call ForGroup(whichGroup, function GCU_AnyGroupAddGroupEnum)
        set udg_GCU_ResultingGroup = CreateGroup()
        set udg_GCU_CurrentX = x
        set udg_GCU_CurrentY = y
        loop
            exitwhen n == 0
            set udg_GCU_CurrentPick = null
            set udg_GCU_CurrentDistance = 100000
            call ForGroup(udg_GCU_AnyGroup, function GCU_Enum)
            exitwhen udg_GCU_CurrentPick == null
            call GroupRemoveUnit(udg_GCU_AnyGroup, udg_GCU_CurrentPick)
            call GroupAddUnit(udg_GCU_ResultingGroup, udg_GCU_CurrentPick)
            set n = n - 1
        endloop
        return udg_GCU_ResultingGroup
    endfunction
   
           // End of GetClosestUnit
//==============================================================================

Attention, carefully read this! You need to create certain variables to get it work properly!
JASS:
 - press  Ctrl+B to open  the variable  editor and create the following
//    variables leaving their initial values to the default ones
//
//              Type        Name
//              Unit        GCU_CurrentPick
//              Real        GCU_CurrentX
//              Real        GCU_CurrentY
//              Real        GCU_CurrentDistance
//              Unit Group  GCU_AnyGroup
//              Unit Group  GCU_ResultingGroup
//
//       - congrats, you are done!
 
Level 19
Joined
Jul 14, 2011
Messages
875
Like IcemanBo said,
use set udg_AF_RangeUnit = GetClosestUnitsInRange(GetUnitX(udg_AF_Target), GetUnitY(udg_AF_Target), 500, 3, Filter(function EnemyFilter))(via custom script) to populate the 'AF_RangeUnit' group with the 3 closest units to the position (which includes AF_Target).

Then you can 'Pick' all the units in the group and do what want to do with them (damage in this case).

Basically what you did here, except you need to replace udg_G with idg_AF_RangeUnit in the custom script.
 
Level 19
Joined
Oct 7, 2014
Messages
2,209
  • Ancestral Flame
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Ancestral Flame
    • Actions
      • Set AF_Target = (Target unit of ability being cast)
      • Set AF_Damage[1] = (Real(((Intelligence of (Triggering unit) (Include bonuses)) x 10)))
      • Set AF_Damage[2] = (Real(((Intelligence of (Triggering unit) (Include bonuses)) x 12)))
      • Set AF_Damage[3] = (Real(((Intelligence of (Triggering unit) (Include bonuses)) x 15)))
      • Custom script: set udg_AF_RangeUnit = GetClosestUnitsInRange(GetUnitX(udg_AF_Target), GetUnitY(udg_AF_Target), 500, 3, Filter(function EnemyFilter))
      • Unit Group - Pick every unit in AF_RangeUnit and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • ((Casting unit) has buff Elemental Grasp ) Equal to True
            • Then - Actions
              • Unit - Cause (Triggering unit) to damage (Picked unit), dealing (AF_Damage[(Level of Ancestral Flame for (Triggering unit))] + 10.00) damage of attack type Spells and damage type Normal
            • Else - Actions
              • Unit - Cause (Triggering unit) to damage (Picked unit), dealing AF_Damage[(Level of Ancestral Flame for (Triggering unit))] damage of attack type Spells and damage type Normal
      • Custom script: call DestroyGroup(udg_AF_RangeUnit)
Sorry for bugging you and IcemanBo and thank you for your help!
 
Status
Not open for further replies.
Top