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

[Trigger] Range Indicator! [Solved]

Status
Not open for further replies.
Level 10
Joined
Jun 20, 2017
Messages
333
Hey, how can I fix this problem?!

If the target is a bit out of range, the tower can still attack the unit!
Pyrogasm said: Yes because the target's collision size is taken into account. If it overlaps in any capacity it's "in range".

Also instead of those many magical runes I want a simple circle.
And I just want it to show the attack range and not the acquisition range. It shouldn't matter what the number is for the acquisition range, it should calculate the attack range.
 

Attachments

  • dota-2-enchantress.jpg
    dota-2-enchantress.jpg
    475.4 KB · Views: 45
  • Screenshot_1.png
    Screenshot_1.png
    1.6 MB · Views: 47
  • Screenshot_2.png
    Screenshot_2.png
    1.6 MB · Views: 39
  • Screenshot_3.png
    Screenshot_3.png
    1.6 MB · Views: 43
  • Range Indicator.w3m
    19.8 KB · Views: 12
Last edited:
Level 12
Joined
Jan 10, 2023
Messages
191
ThompZon is correct, I tested this when I first started the grid system (back when I was mistaking exactly what you wanted for your range system )

I DID NOT update that in the map you made that I made some changes to.
- It looks like you have a triggered-range that you have set and maybe this is to get rid of the annoyance of Acquisition Range, either way, I figured you may just want to add the collision size to the pre-set value you made

Although I will say this about Acquisition Range:
Nothing can attack further than its acquisition range, therefore you might as well set this as equal to the attack range for all of your building units (and for moving units, you may want acquisition range higher than attack range) imo
Then you wouldn't have to set a variable for each unit type's range.

I added one variable image array, imported an image to serve as your circle, and I updated a trigger...

I think in the past you didn't want a solid circle, so if that is still the case, you'll have to find a circle that's right for you, I dug around a little to try to find the selection circle path but I didn't have any luck... some days I swear it jumps out at me, other days I can search all day and not find what I'm looking for... lol

The Trigger I changed:
  • Untitled Trigger 002
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Check Range (TOWER RANGE)
      • (Level of Tower Classification [Range] for (Triggering unit)) Equal to 1
    • Actions
      • Set VariableSet Points_Range[1] = (Position of (Triggering unit))
      • -------- --------
      • For each (Integer Integer_LoopRange) from 1 to Integer_Range, do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Unit-type of (Triggering unit)) Equal to UnitTypes[Integer_LoopRange]
            • Then - Actions
              • Set VariableSet Real = Reals_Range[Integer_LoopRange]
            • Else - Actions
      • -------- --------
      • Set VariableSet Points_Range[0] = (Points_Range[1] offset by ((0.00 - Real), (0.00 - Real)))
      • Image - Destroy Range_Image[(Player number of (Triggering player))]
      • Custom script: set udg_Range_Image[ GetConvertedPlayerId( GetTriggerPlayer() ) ]= CreateImage( "war3mapImported\\Circle1024", 2*udg_Real, 2*udg_Real, 2*udg_Real, GetLocationX( udg_Points_Range[1] ), GetLocationY( udg_Points_Range[1] ), 0, udg_Real, udg_Real, 0, 3 )
      • Image - Change Range_Image[(Player number of (Triggering player))]: Enable render always state
      • -------- PUT COLOR CHANGE BELOW THIS LINE --------
      • -------- PUT COLOR CHANGE ABOVE THIS LINE --------
      • Custom script: if GetLocalPlayer() == GetTriggerPlayer() then
      • Image - Show Range_Image[(Player number of (Triggering player))]
      • Custom script: endif
      • For each (Integer Integer_LoopRange) from 1 to 36, do (Actions)
        • Loop - Actions
          • Set VariableSet Points_Range[2] = (Points_Range[1] offset by Real towards (10.00 x (Real(Integer_LoopRange))) degrees.)
          • Unit - Create 1 Check Range [Dummy] for (Owner of (Triggering unit)) at Points_Range[2] facing Default building facing degrees
          • Unit - Add a 5.00 second Generic expiration timer to (Last created unit)
          • Custom script: call RemoveLocation(udg_Points_Range[2])
      • Custom script: call RemoveLocation(udg_Points_Range[1])
      • Custom script: call RemoveLocation(udg_Points_Range[0])
Comments on the Trigger:
As you can see, the image array variable it called 'Range_Image' (or udg_Range_Image if we use it in a custom script/ JASS ever)
index 1-12 represent the players
We have to have an image for each player because images can't change sizes and each range will be a different size, so each player needs their own and each time the make one, they need to make a new one and set the size accordingly.

We use
  • Custom script: if GetLocalPlayer() == GetTriggerPlayer() then
  • Image - Show Range_Image[(Player number of (Triggering player))]
  • Custom script: endif
to make sure we only show the one player who used the ability the range.
If you want everyone to see, remove this line and remove the custom script before and after showing the image.

I uploaded my version so that you would have my circle image.
EDIT:
Get Paint.net (can save .tga) and blpLab (both are free) to have a really easy time making your own 2d images... I could make you a bunch of circles, or you could make the exact one(s) you need this way.

I made some changes so that the image's origin is the center of the image; by default (and always in GUI) and image's origin is its bottom-left corner.
Using a little bit of JASS to create the image, we can change that.

The game will hide an image if the image's origin is off-screen, so this is necessary if you want the image to stay visible if that player pans the screen.
Without this change, the image will stay until it is fully off-screen on the right-side of the view, but on the left side of the view it would disappear as soon as a small part of it was off-screen.
 

Attachments

  • Range Indicator (some changes by Tristronic).w3m
    142.7 KB · Views: 14
Last edited:
Level 24
Joined
Feb 9, 2009
Messages
1,787
I went over this when I realized it isn't common knowledge.
TL;DR Use:
  • Unit Group - Pick every unit in ((Your AOE value) + (Maximum Collision size in gameplayconstants)) of TempPoint.) and do (Actions)
    • Loop - Actions
      • Custom script: if IsUnitInRangeLoc(GetEnumUnit(), udg_TempPoint, udg_Your_Aoe_Value then
      • (Your actions)
      • Custom script: endif
That way you can have you're triggered spells match the standard of vanilla spells that also match the range indicators.
2dgxg9-png.392396




@Tristronic
Holy guac I'm learning something new!
 
Level 12
Joined
Jan 10, 2023
Messages
191
@Tristronic
Holy guac I'm learning something new!

Well if you're gonna keep messing around with stuff relating to collision size as it relates to unit location, maybe this will save you some trouble...
I made a knockback system in GUI before I started learning JASS, I plan to remake it in JASS, but in doing so I learned this the hard way:
In the context of pathing collision size works like this:
Collision Size Map.png

Each little square is 16.00 x 16.00 warcraft dist-o-meters

Yet for purposes of combat and range, collision size uses the exact real value.

Granted, there are systems out there that manage to work around this (using code that doesn't consider it and works by different means), but this knowledge may come in handy in other places.
 
Last edited:
Level 10
Joined
Jun 20, 2017
Messages
333
I'd assume that you haven't taken the tower's collision size into consideration? (I'm not 100% sure that's a thing, I haven't fiddled with anything like this where it matters).
ThompZon said exactly what I would say. I didn't even consider that in my other reply, but it probably does apply.
This way?!
Set VariableSet Points_Range[2] = (Points_Range[1] offset by Real towards (10.00 x ((Real(Integer_LoopRange)) + Reals_CollisionSize[Integer_LoopRange])) degrees.)
Set VariableSet Points_Range[2] = (Points_Range[1] offset by Real towards (10.00 x ((Real(Integer_LoopRange)) + (Collision Size of (Triggering unit)))) degrees.)

I think in the past you didn't want a solid circle, so if that is still the case, you'll have to find a circle that's right for you, I dug around a little to try to find the selection circle path but I didn't have any luck... some days I swear it jumps out at me, other days I can search all day and not find what I'm looking for... lol
No I don't want a solid circle, I want a circle like the selection circle but the white version. Yes, I looked for that too, didn't find what I was looking for! Maybe I'll make a request for it.

same issue with your map?!

Maximum Collision size in gameplayconstants
How did you do that?!
  • Untitled Trigger 003
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Check Range (TOWER RANGE)
      • (Level of Tower Classification [Range] for (Triggering unit)) Equal to 1
    • Actions
      • Set VariableSet Points_Range[1] = (Position of (Triggering unit))
      • -------- --------
      • Unit Group - Pick every unit in (Units within Reals_Range[Integer_Range] of Points_Range[1].) and do (Actions)
        • Loop - Actions
          • Custom script: if IsUnitInRange(GetEnumUnit(), GetTriggerUnit(), udg_Reals_Range[Integer_Range]) then
          • Unit - Create 1 Check Range [Dummy] for (Owner of (Picked unit)) at Points_Range[1] facing Default building facing degrees
          • Unit - Add a 5.00 second Generic expiration timer to (Last created unit)
          • Custom script: endif
      • Custom script: call RemoveLocation(udg_Points_Range[1])
this line can't run!
Custom script: if IsUnitInRange(GetEnumUnit(), GetTriggerUnit(), udg_Reals_Range[Integer_Range]) then
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,543
This way?!
Set VariableSet Points_Range[2] = (Points_Range[1] offset by Real towards (10.00 x ((Real(Integer_LoopRange)) + Reals_CollisionSize[Integer_LoopRange])) degrees.)
Set VariableSet Points_Range[2] = (Points_Range[1] offset by Real towards (10.00 x ((Real(Integer_LoopRange)) + (Collision Size of (Triggering unit)))) degrees.)


No I don't want a solid circle, I want a circle like the selection circle but the white version. Yes, I looked for that too, didn't find what I was looking for! Maybe I'll make a request for it.

same issue with your map?!


How did you do that?!
  • Untitled Trigger 003
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Check Range (TOWER RANGE)
      • (Level of Tower Classification [Range] for (Triggering unit)) Equal to 1
    • Actions
      • Set VariableSet Points_Range[1] = (Position of (Triggering unit))
      • -------- --------
      • Unit Group - Pick every unit in (Units within Reals_Range[Integer_Range] of Points_Range[1].) and do (Actions)
        • Loop - Actions
          • Custom script: if IsUnitInRange(GetEnumUnit(), GetTriggerUnit(), udg_Reals_Range[Integer_Range]) then
          • Unit - Create 1 Check Range [Dummy] for (Owner of (Picked unit)) at Points_Range[1] facing Default building facing degrees
          • Unit - Add a 5.00 second Generic expiration timer to (Last created unit)
          • Custom script: endif
      • Custom script: call RemoveLocation(udg_Points_Range[1])
this line can't run!
Custom script: if IsUnitInRange(GetEnumUnit(), GetTriggerUnit(), udg_Reals_Range[Integer_Range]) then
You need to add "udg_" before the name of your global variables when you reference them in Custom script.

Integer_Range -> udg_Integer_Range

This is how the game knows that you're referencing a variable created in the standard trigger editor and not a "coded" variable.
 
Last edited:
Level 24
Joined
Feb 9, 2009
Messages
1,787
@AlwaysAndForever
Use this instead so you don't need to reference the (Triggering unit) which will be lost after the first unit picked:

  • Custom script: if IsUnitInRangeLoc(GetEnumUnit(), udg_Points_Range[1], udg_Reals_Range[udg_Integer_Range]) then
For the gameplay constants, Scroll down until you find maximum collision (Which by default is 200.00 iirc):
1677943971270.png
 
Last edited:
Level 39
Joined
Feb 27, 2007
Messages
5,016
Use this instead so you don't need to reference the (Triggering unit) which will be lost after the first unit picked:
They’re not trying to check if a specific unit is in range, they want to display an accurate range circle around a unit that shows where units would be in range.

@AlwaysAndForever I see those two lines multiply the collision radius by 10, which I don’t think you want. You should add collision size after/outside of the LoopInt multiplication stuff.
 
Last edited:
Level 10
Joined
Jun 20, 2017
Messages
333
You should add collision size after/outside of the LoopInt multiplication stuff.
But I added them in another trigger! should I add another variable for x/y position?!
  • Untitled Trigger 001
    • Events
      • Time - Elapsed game time is 0.01 seconds
    • Conditions
    • Actions
      • Set VariableSet Integer_Range = (Integer_Range + 1)
      • Set VariableSet UnitTypes[Integer_Range] = Guard Tower
      • Set VariableSet Reals_TowerRange[Integer_Range] = 400.00
      • Set VariableSet Reals_CollisionSize[Integer_Range] = 72.00
      • -------- --------
      • Set VariableSet Integer_Range = (Integer_Range + 1)
      • Set VariableSet UnitTypes[Integer_Range] = Cannon Tower
      • Set VariableSet Reals_TowerRange[Integer_Range] = 600.00
      • Set VariableSet Reals_CollisionSize[Integer_Range] = 72.00
      • -------- --------
      • Set VariableSet Integer_Range = (Integer_Range + 1)
      • Set VariableSet UnitTypes[Integer_Range] = Arcane Tower
      • Set VariableSet Reals_TowerRange[Integer_Range] = 800.00
      • Set VariableSet Reals_CollisionSize[Integer_Range] = 72.00
  • Untitled Trigger 003
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Check Range (TOWER RANGE)
      • (Level of Tower Classification [Range] for (Triggering unit)) Equal to 1
    • Actions
      • Set VariableSet Points_Range[1] = (Position of (Triggering unit))
      • -------- --------
      • Custom script: set bj_wantDestroyGroup = true
      • Unit Group - Pick every unit in (Units within Reals_TowerRange[Integer_Range] of Points_Range[1].) and do (Actions)
        • Loop - Actions
          • Custom script: if IsUnitInRange(GetEnumUnit(), GetTriggerUnit(), udg_Reals_TowerRange[udg_Integer_Range] + udg_Reals_CollisionSize[udg_Integer_Range]) then
          • Unit - Create 1 Check Range [Dummy] for (Owner of (Picked unit)) at Points_Range[1] facing Default building facing degrees
          • Unit - Add a 5.00 second Generic expiration timer to (Last created unit)
          • Custom script: endif
      • Custom script: call RemoveLocation(udg_Points_Range[1])

I have an idea, what if I turn collision off/on? so this way I don't have to calculate the collision size of the towers!
For example, when I use the ability, the collision for the tower should be off, and then when the effect is gone, turn it on again?!
  • Untitled Trigger 003
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Check Range (TOWER RANGE)
      • (Level of Tower Classification [Range] for (Triggering unit)) Equal to 1
    • Actions
      • Unit - Turn collision for (Triggering unit) Off.
      • Set VariableSet Points_Range[1] = (Position of (Triggering unit))
      • -------- --------
      • Custom script: set bj_wantDestroyGroup = true
      • Unit Group - Pick every unit in (Units within Reals_TowerRange[Integer_Range] of Points_Range[1].) and do (Actions)
        • Loop - Actions
          • Custom script: if IsUnitInRange(GetEnumUnit(), GetTriggerUnit(), udg_Reals_TowerRange[udg_Integer_Range] + udg_Reals_CollisionSize[udg_Integer_Range]) then
          • Unit - Create 1 Check Range [Dummy] for (Owner of (Picked unit)) at Points_Range[1] facing Default building facing degrees
          • Unit - Add a 5.00 second Generic expiration timer to (Last created unit)
          • Custom script: endif
          • Unit - Turn collision for (Picked unit) On.
      • Custom script: call RemoveLocation(udg_Points_Range[1])
 

Attachments

  • Range Indicator (some changes by Tristronic).w3m
    149 KB · Views: 4
Level 12
Joined
Jan 10, 2023
Messages
191
Turning collision off just makes it so that other units don't check for it when moving and allows the unit to be placed in unwalkable places.

Adding the collision size in the pre-set ranges you have there in the trigger is sufficient, assuming those 400/600/800 are including the collision size.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,543
An efficient and fast optimization that can be made here is to use a Hashtable. You would store the Unit-Type id of your Towers to a Hashtable as the Key and their "Integer_Range" as the Value. What's great is that you've already gotten 90% of the way there with your current design so it's a really easy addition to make.

I've attached a map below with these changes implemented and here are the triggers. Note that I renamed things for consistency:
  • Setup Towers
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Hashtable - Create a hashtable
      • Set VariableSet Range_Hash = (Last created hashtable)
      • -------- --------
      • Set VariableSet Range_Index = (Range_Index + 1)
      • Set VariableSet Range_UnitTypes[Range_Index] = Guard Tower
      • Set VariableSet Range_TowerRange[Range_Index] = 400.00
      • Set VariableSet Range_CollisionSize[Range_Index] = 72.00
      • -------- --------
      • Set VariableSet Range_Index = (Range_Index + 1)
      • Set VariableSet Range_UnitTypes[Range_Index] = Cannon Tower
      • Set VariableSet Range_TowerRange[Range_Index] = 600.00
      • Set VariableSet Range_CollisionSize[Range_Index] = 72.00
      • -------- --------
      • Set VariableSet Range_Index = (Range_Index + 1)
      • Set VariableSet Range_UnitTypes[Range_Index] = Arcane Tower
      • Set VariableSet Range_TowerRange[Range_Index] = 800.00
      • Set VariableSet Range_CollisionSize[Range_Index] = 72.00
      • -------- --------
      • For each (Integer Range_Loop) from 1 to Range_Index, do (Actions)
        • Loop - Actions
          • -------- We've now linked our tower's Unit-Type Id to it's Range_Index [index]: --------
          • Custom script: set udg_Range_Id = udg_Range_UnitTypes[udg_Range_Loop]
          • Hashtable - Save Range_Loop as 0 of Range_Id in Range_Hash.
  • Check Tower Range
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Check Range (TOWER RANGE)
    • Actions
      • Set VariableSet Range_Tower = (Triggering unit)
      • Set VariableSet Range_Point = (Position of Range_Tower)
      • Custom script: set udg_Range_Index = LoadInteger(udg_Range_Hash, GetUnitTypeId(udg_Range_Tower), 0)
      • -------- --------
      • -------- Add the range + collision size together for efficiency: --------
      • Set VariableSet Range_TowerRangeFixed = (Range_TowerRange[Range_Index] + Range_CollisionSize[Range_Index])
      • -------- --------
      • Custom script: set bj_wantDestroyGroup = true
      • Unit Group - Pick every unit in (Units within Range_TowerRange[Range_Index] of Range_Point.) and do (Actions)
        • Loop - Actions
          • Set VariableSet Range_Target = (Picked unit)
          • -------- --------
          • Custom script: if IsUnitInRange( udg_Range_Target, udg_Range_Tower, udg_Range_TowerRangeFixed ) then
          • -------- --------
          • -------- Found a valid target: --------
          • Set VariableSet Range_Point2 = (Position of Range_Target)
          • Unit - Create 1 Check Range [Dummy] for (Owner of Range_Tower) at Range_Point2 facing Default building facing degrees
          • Unit - Add a 5.00 second Generic expiration timer to (Last created unit)
          • Custom script: call RemoveLocation(udg_Range_Point2)
          • -------- --------
          • Custom script: endif
      • -------- --------
      • Custom script: call RemoveLocation(udg_Range_Point)
 

Attachments

  • Range Indicator (some changes by Tristronic) U1.w3m
    147.8 KB · Views: 8
Last edited:
Level 10
Joined
Jun 20, 2017
Messages
333
Hey Uncle, thank you, however there are some problems!

1. The size of the circle doesn't change, which I think I should add this line after a dummy created!
Animation - Change (Last created unit)'s size to (Range_TowerRangeFixed%, Range_TowerRangeFixed%, Range_TowerRangeFixed%) of its original size
2. If I use the guard tower ability, it creates a circle around it, which is fine, but if the unit is not in range, it will still attack it!
3. If I use the ability of other units, it will make it weird or create a multiple circle!

I am going to post a request for a simple circle.
 

Attachments

  • 1.png
    1.png
    1.5 MB · Views: 16
  • 2.png
    2.png
    1.5 MB · Views: 17
  • 3.png
    3.png
    1.5 MB · Views: 13
  • 4.png
    4.png
    1.1 MB · Views: 12
  • 5.png
    5.png
    1.2 MB · Views: 17
Last edited:
Level 12
Joined
Jan 10, 2023
Messages
191
Is Untitled Trigger 002 still a thing? I'm confused, I see a dummy being made for each unit in range but I see nothing stopping an attack out of range and nothing that displays the range.

I do appreciate the naming of the triggers btw, Uncle
 
Level 39
Joined
Feb 27, 2007
Messages
5,016
@AlwaysAndForever I think you are massively overcomplicating this and are getting confused. It really is this simple:
  1. If you want to learn if a specific unit is within range of a specific other unit you simply need to use IsUnitInRange() like you are doing. This function takes two units as an input and a range value. That range value does not need to have collision size added in; this function accounts for the collision size of both units automatically.

  2. If you want to find a series of points that mark the edge of a circle with a particular range around a particular unit, you will need to account for that source unit's collision size when using polar projection (GUI calls this "point with polar offset") to compute these locations. You will thus project out by RANGE + SOURCE_COLLISION_SIZE. Any unit whose collision circle partially overlaps these points will be "in range" to be attacked by a tower with RANGE range.

  3. If you want to find every unit within a certain range of another specific unit, you can accomplish this in two different ways:
    1. Grab every unit within RANGE + (2 x MAX_MAP_COLLISION_SIZE) of the location of the source unit, and then filter down this list of units by checking IsUnitInRange on each one paired with the source unit, inputting RANGE as the search range.
    2. Grab every unit within RANGE + (2 x MAX_MAP_COLLISION_SIZE) of the location of the source unit, and then filter down this list of units by checking IsUnitInRangeLoc on each one, inputting RANGE + SOURCE_COLLISION_SIZE as the search range.
Animation - Change (Last created unit)'s size to (Range_TowerRangeFixed%, Range_TowerRangeFixed%, Range_TowerRangeFixed%) of its original size
Size is not a numerical representation of how many pixels wide or even wc3 distance-units wide the object is. It's a percentage scalar relative to the default size of the model as it was created. This method would work if and only if the model was exactly the size of 1 wc3 distance-unit by default. It could also be some known multiple of 1 (like 10 or 100 or 64) which would work for computations almost as easily.
 
Level 10
Joined
Jun 20, 2017
Messages
333
I think you are massively overcomplicating this and are getting confused.
Yeah I just can't fix/modify the size of the ability range, which if the units are in the range of the ability(circle), then the tower should attack them not when they are not in range (circle)!

Hell Circle - v.0.03
I think I need something like this, which if you cast the spell, it creates a circle of fire around the caster, and when he moves and finds his target, the unit that comes into contact with the circle is attacked.

I changed everything either the circle is created in a random place or it creates the circle to a random point of collision size!
But that's not the problem, it still can't create a decent sized circle!
Set VariableSet Point_PolarOffset = (Range_Point offset by Range_CollisionSize[Range_Index] towards Range_TowerRangeFixed degrees.)
Set VariableSet Point_PolarOffset = (Range_Point offset by 0.00 towards (Angle from Range_Point to Range_Point) degrees.)
Set VariableSet Point_PolarOffset = (Range_Point offset by 0.00 towards Range_TowerRangeFixed degrees.)
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,543
Hey Uncle, thank you, however there are some problems!

1. The size of the circle doesn't change, which I think I should add this line after a dummy created!
Animation - Change (Last created unit)'s size to (Range_TowerRangeFixed%, Range_TowerRangeFixed%, Range_TowerRangeFixed%) of its original size
2. If I use the guard tower ability, it creates a circle around it, which is fine, but if the unit is not in range, it will still attack it!
3. If I use the ability of other units, it will make it weird or create a multiple circle!

I am going to post a request for a simple circle.
Yeah, I had no idea what you actually wanted so I simply fixed your errors and got what you presented up and running. I think it's still a good foundation for what you want though. Anyway, I don't think you should worry about a ~10-20 range imprecision, players will certainly not care.
 
Last edited:
Level 39
Joined
Feb 27, 2007
Messages
5,016
Yeah I just can't fix/modify the size of the ability range, which if the units are in the range of the ability(circle), then the tower should attack them not when they are not in range (circle)!

Hell Circle - v.0.03
I think I need something like this, which if you cast the spell, it creates a circle of fire around the caster, and when he moves and finds his target, the unit that comes into contact with the circle is attacked.

I changed everything either the circle is created in a random place or it creates the circle to a random point of collision size!
But that's not the problem, it still can't create a decent sized circle!
Set VariableSet Point_PolarOffset = (Range_Point offset by Range_CollisionSize[Range_Index] towards Range_TowerRangeFixed degrees.)
Set VariableSet Point_PolarOffset = (Range_Point offset by 0.00 towards (Angle from Range_Point to Range_Point) degrees.)
Set VariableSet Point_PolarOffset = (Range_Point offset by 0.00 towards Range_TowerRangeFixed degrees.)
2. If you want to find a series of points that mark the edge of a circle with a particular range around a particular unit, you will need to account for that source unit's collision size when using polar projection (GUI calls this "point with polar offset") to compute these locations. You will thus project out by RANGE + SOURCE_COLLISION_SIZE. Any unit whose collision circle partially overlaps these points will be "in range" to be attacked by a tower with RANGE range.
I don't mean to beat a dead horse here but...
I think you are massively overcomplicating this
  • Set SourceUnit = (Triggering Unit)
  • Set SourcePoint = (Position of SourceUnit)
  • Set Range = SOME_WAY_TO_GET_ATTACK_RANGE_OF_THE_SOURCEUNIT
  • Set Collision = SOME_WAY_TO_GET_COLLISION_SIZE_OF_THE_SOURCEUNIT
  • Set N = 100
  • Set AngleInc = (360.00 / N)
  • For each (Integer A) from 1 to N do (Actions)
    • Loop - Actions
      • Set EdgePoint = (SourcePoint offset by (Range + Collision) towards (AngleInc x (Real((Integer A)))) degrees
      • Special Effect - Create an effect at EdgePoint using ...
      • Set RangeEffects[(Integer A)] = (Last created special effect)
      • Custom script: call RemoveLocation(udg_EdgePoint)
  • Custom script: call RemoveLocation(udg_SourcePoint)
  • // do whatever with RangeEffects[]
 
Level 10
Joined
Jun 20, 2017
Messages
333
Thanks all. It is solved now.
As you can see in the pictures below, the Footman (right side) leg is inside of the circle but he still cannot be targeted, and I tested it with the Rifleman which was fine, I think it is only the Footman that has a problem or he just too big!
Close enough tho.

I swear I did it yesterday but the wrong way!
Instead of this
Animation - Change LastCreatedUnit's size to ((Real_TotalTowerRange x 2.00)%, (Real_TotalTowerRange x 2.00)%, 100.00%) of its
this
Animation - Change LastCreatedUnit's size to ((Real_TotalTowerRange)%, (Real_TotalTowerRange)%, 100.00%) of its
  • Setup Towers
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set VariableSet Integer_TowerRangeIndex = (Integer_TowerRangeIndex + 1)
      • Set VariableSet UnitTypes_TowerRange[Integer_TowerRangeIndex] = Guard Tower
      • Set VariableSet Reals_TowerRangeSize[Integer_TowerRangeIndex] = 400.00
      • Set VariableSet Reals_CollisionSize[Integer_TowerRangeIndex] = 72.00
      • Set VariableSet Real_TotalTowerRange = (Reals_TowerRangeSize[Integer_TowerRangeIndex] + Reals_CollisionSize[Integer_TowerRangeIndex])
      • -------- --------
      • Set VariableSet Integer_TowerRangeIndex = (Integer_TowerRangeIndex + 1)
      • Set VariableSet UnitTypes_TowerRange[Integer_TowerRangeIndex] = Cannon Tower
      • Set VariableSet Reals_TowerRangeSize[Integer_TowerRangeIndex] = 600.00
      • Set VariableSet Reals_CollisionSize[Integer_TowerRangeIndex] = 72.00
      • Set VariableSet Real_TotalTowerRange = (Reals_TowerRangeSize[Integer_TowerRangeIndex] + Reals_CollisionSize[Integer_TowerRangeIndex])
      • -------- --------
      • Set VariableSet Integer_TowerRangeIndex = (Integer_TowerRangeIndex + 1)
      • Set VariableSet UnitTypes_TowerRange[Integer_TowerRangeIndex] = Arcane Tower
      • Set VariableSet Reals_TowerRangeSize[Integer_TowerRangeIndex] = 800.00
      • Set VariableSet Reals_CollisionSize[Integer_TowerRangeIndex] = 72.00
      • Set VariableSet Real_TotalTowerRange = (Reals_TowerRangeSize[Integer_TowerRangeIndex] + Reals_CollisionSize[Integer_TowerRangeIndex])
  • Check Tower Range
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Check Range
    • Actions
      • Set VariableSet TriggeringUnit = (Triggering unit)
      • Set VariableSet Point_TowerRange = (Position of TriggeringUnit)
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of TriggeringUnit) Equal to Guard Tower
        • Then - Actions
          • -------- Add the range + collision size together for efficiency: --------
          • Set VariableSet Reals_TowerRangeSize[Integer_TowerRangeIndex] = 400.00
          • Set VariableSet Reals_CollisionSize[Integer_TowerRangeIndex] = 72.00
          • Set VariableSet Real_TotalTowerRange = (Reals_TowerRangeSize[Integer_TowerRangeIndex] + Reals_CollisionSize[Integer_TowerRangeIndex])
          • -------- --------
          • Unit - Create 1 Check Range [Dummy] for (Owner of TriggeringUnit) at Point_TowerRange facing Default building facing degrees
          • Set VariableSet LastCreatedUnit = (Last created unit)
          • Animation - Change LastCreatedUnit's size to ((Real_TotalTowerRange x 2.00)%, (Real_TotalTowerRange x 2.00)%, 100.00%) of its original size
          • Unit - Add a 5.00 second Generic expiration timer to LastCreatedUnit
        • Else - Actions
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of TriggeringUnit) Equal to Cannon Tower
        • Then - Actions
          • -------- Add the range + collision size together for efficiency: --------
          • Set VariableSet Reals_TowerRangeSize[Integer_TowerRangeIndex] = 600.00
          • Set VariableSet Reals_CollisionSize[Integer_TowerRangeIndex] = 72.00
          • Set VariableSet Real_TotalTowerRange = (Reals_TowerRangeSize[Integer_TowerRangeIndex] + Reals_CollisionSize[Integer_TowerRangeIndex])
          • -------- --------
          • Unit - Create 1 Check Range [Dummy] for (Owner of TriggeringUnit) at Point_TowerRange facing Default building facing degrees
          • Set VariableSet LastCreatedUnit = (Last created unit)
          • Animation - Change LastCreatedUnit's size to ((Real_TotalTowerRange x 2.00)%, (Real_TotalTowerRange x 2.00)%, 100.00%) of its original size
          • Unit - Add a 5.00 second Generic expiration timer to LastCreatedUnit
        • Else - Actions
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of TriggeringUnit) Equal to Arcane Tower
        • Then - Actions
          • -------- Add the range + collision size together for efficiency: --------
          • Set VariableSet Reals_TowerRangeSize[Integer_TowerRangeIndex] = 800.00
          • Set VariableSet Reals_CollisionSize[Integer_TowerRangeIndex] = 72.00
          • Set VariableSet Real_TotalTowerRange = (Reals_TowerRangeSize[Integer_TowerRangeIndex] + Reals_CollisionSize[Integer_TowerRangeIndex])
          • -------- --------
          • Unit - Create 1 Check Range [Dummy] for (Owner of TriggeringUnit) at Point_TowerRange facing Default building facing degrees
          • Set VariableSet LastCreatedUnit = (Last created unit)
          • Animation - Change LastCreatedUnit's size to ((Real_TotalTowerRange x 2.00)%, (Real_TotalTowerRange x 2.00)%, 100.00%) of its original size
          • Unit - Add a 5.00 second Generic expiration timer to LastCreatedUnit
        • Else - Actions
      • -------- --------
      • Custom script: call RemoveLocation(udg_Point_TowerRange)
 

Attachments

  • Screenshot_12.png
    Screenshot_12.png
    1.6 MB · Views: 20
  • Screenshot_13.png
    Screenshot_13.png
    1.6 MB · Views: 19
  • Screenshot_14.png
    Screenshot_14.png
    1.6 MB · Views: 17
  • Screenshot_15.png
    Screenshot_15.png
    1.6 MB · Views: 19
  • Range Indicator.w3m
    31.4 KB · Views: 8
Last edited:
Level 12
Joined
Jan 10, 2023
Messages
191
Well it looks like you're satisfied, so we could leave it at that, but if you want to go down a rabbit-hole with me:

This must feed into a third trigger, what's that trigger look like?
I know we've all seen it kicking around above, but did that change?
It's just tricky to comment on in detail because we can see that it clearly fires on the same event, but it is so much related that one would think it should be right her with the rest of these actions in "Check Tower Range".

I'm curious because I would think you could simplify and then combine the trigger that draws the circle, in which case you make be able to get rid of those globals (I don't see the point in setting those values), but I might have done something like this, depending on what the remaining triggers/actions look like:
  • Check Tower Range
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Acid Bomb
    • Actions
      • Set VariableSet Point_TowerRange = (Position of (Triggering unit))
      • Unit - Create 1 Check Range [Dummy] for (Owner of (Triggering unit)) at Point_TowerRange facing Default building facing degrees
      • Animation - Change (Last created unit)'s size to (100.00%, 100.00%, 100.00%) of its original size
      • Unit - Add a 5.00 second Generic expiration timer to (Last created unit)
      • Custom script: call RemoveLocation( udg_Point_TowerRange )
      • Custom script: call TheHiddenActions()

Does the dummy do something? The circle looks like an image, not a model/dummy.

About the animation action:
I don't think that animation action is doing anything useful, like Pyrogasm said, those sizes a relative to the model size.
A 100%, 100%, 100% Pit Lord is much bigger than a 100%, 100%, 100% Peon, and none of us would trust that the size of said unit is going to translate to Wc3 distance units in a useful way.
Range isn't related to the size of a unit, but you've set the size of the dummy in terms of the range of the checked tower.
If the tower has a range of 800.00, your dummy is set to be 8 times it's normal size... why?

About the global variables:
Now of course we don't have the values saved, but those values are all stored in the unit itself, the only thing I would think we would have to store is the location of the triggering unit, so that we can remove the location when we're done, and we may want to store the triggering unit, but I don't think we should want to because:
  • If some other trigger wants to know the triggering unit, we need to call that trigger now or never, or we need to make a variable to be a handle for that triggering unit; otherwise some other triggering unit is going to be mistaken for our triggering unit if some other player checks their unit's range in the meantime, I'm guessing that's why you made the variable, to avoid this.
  • If there is more to the sequence of actions than we see here, we could probably just put those actions right here in the same trigger; ie: drawing that circle could go right in 'Check Tower Range' after those first actions. Unfortunately, because the trigger is separated we don't know what is going on here, we see triggers and pictures, and they seem to have little to do with the one another.
Why make a LastCreatedUnit variable? there is (Last created unit) for that, and maybe there is a time and a place for something like this but the dummy is dead in 5 seconds, does anything else happen to it that we need to remember the dummy in a variable?

We won't need to save many of those values because we can use:
  • Set VariableSet Reals_TotalTowerRange = (Unit: (Triggering unit)'s Weapon Real Field: Attack Range ('ua1m') at Index:0) + (Collision Size of (Triggering unit))
With that being said, we shouldn't need to have a variable called 'Reals_TotalTowerRange' either, unless you will be using that value multiple times, in which case you may want the variable to save you from typing/ clicking a lot.
  • You could use this instead of 'Reals_TotalTowerRange' -> (Unit: (Triggering unit)'s Weapon Real Field: Attack Range ('ua1m') at Index:0) + (Collision Size of (Triggering unit))
If you are only going to call the value one time, I would forget the variable (you don't really have to, but in a sense its dead weight, albeit light dead weight), if you plan to use it multiple times in the same trigger, then for ease of access I would save this one variable if it made things hard to read or confusing to me, but ditch all of the rest except the location, we really don't care what the range and collision size are individually, we want the sum.

I'm not sure if you remember, but back when we were working on something similar to this, the only reason we didn't use the unit's real field was because of an issue it had with the event we were using: The "Unit - A unit Finishes an upgrade" event.

When the unit upgraded, at the moment when the upgrade trigger fires, the game wrongly averaged the before and after ranges and ruins our range check. If the grid lasts 5 seconds and it doesn't need to be refreshed until the next time the ability is used, we can just check the unit and see what its range is. We would have to be magicians to find that exact instant when the game gives the averaged value like it did with the other event. Without the "unit - finished upgrade" event, we don't need to worry about it anymore. Additionally, because this works when an ability is cast, we can rest assured that the range check cannot possibly happen immediately after the tower finishes upgrading because the unit will not be able to cast the ability until it is upgraded anyway.

This is just another reason to get rid of all the variables you have there except "Point_TowerRange" and "Real_TotalTowerRange", and "Real_TotalTowerRange" is optional.
 
Level 10
Joined
Jun 20, 2017
Messages
333
I don't think that animation action is doing anything useful
Then how can I change the size of the circle? which can be exactly the size of a tower range!
like Pyrogasm said, those sizes a relative to the model size.
I don't know the size of the model! but I made a dummy with size of 1 which is by default.

I would love to have a simple trigger like this! but it doesn't work and shows the same circle for all towers!
the second trigger, 1st and 2nd if not working properly except 3rd if!
  • Check Tower Range
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Check Range
    • Actions
      • Set VariableSet Point_TowerRange = (Position of (Triggering unit))
      • -------- --------
      • Unit - Create 1 Check Range [Dummy] for (Owner of (Triggering unit)) at Point_TowerRange facing Default building facing degrees
      • Animation - Change (Last created unit)'s size to (100.00%, 100.00%, 100.00%) of its original size
      • Unit - Add a 5.00 second Generic expiration timer to (Last created unit)
      • -------- --------
      • Custom script: call RemoveLocation(udg_Point_TowerRange)
  • Check Tower Range Copy Copy
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Check Range
    • Actions
      • Set VariableSet Point_TowerRange = (Position of (Triggering unit))
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of (Triggering unit)) Equal to Guard Tower
        • Then - Actions
          • Unit - Create 1 Check Range [Dummy] for (Owner of (Triggering unit)) at Point_TowerRange facing Default building facing degrees
          • Animation - Change (Last created unit)'s size to (((Unit: (Triggering unit)'s Weapon Real Field: Attack Range ('ua1m') at Index:0) + (Collision Size of (Triggering unit)))%, ((Unit: (Triggering unit)'s Weapon Real Field: Attack Range ('ua1m') at Index:0) + (Collision Size of (Triggering unit)))%, 100.00%) of its original size
          • Unit - Add a 5.00 second Generic expiration timer to (Last created unit)
        • Else - Actions
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of (Triggering unit)) Equal to Cannon Tower
        • Then - Actions
          • Unit - Create 1 Check Range [Dummy] for (Owner of (Triggering unit)) at Point_TowerRange facing Default building facing degrees
          • Animation - Change (Last created unit)'s size to (100.00%, 100.00%, 100.00%) of its original size
          • Unit - Add a 5.00 second Generic expiration timer to (Last created unit)
        • Else - Actions
      • -------- --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of (Triggering unit)) Equal to Arcane Tower
        • Then - Actions
          • -------- Add the range + collision size together for efficiency: --------
          • Set VariableSet Reals_TowerRangeSize[Integer_TowerRangeIndex] = 800.00
          • Set VariableSet Reals_CollisionSize[Integer_TowerRangeIndex] = 72.00
          • Set VariableSet Real_TotalTowerRange = (Reals_TowerRangeSize[Integer_TowerRangeIndex] + Reals_CollisionSize[Integer_TowerRangeIndex])
          • -------- --------
          • Unit - Create 1 Check Range [Dummy] for (Owner of (Triggering unit)) at Point_TowerRange facing Default building facing degrees
          • Set VariableSet LastCreatedUnit = (Last created unit)
          • Animation - Change LastCreatedUnit's size to ((Real_TotalTowerRange x 2.00)%, (Real_TotalTowerRange x 2.00)%, 100.00%) of its original size
          • Unit - Add a 5.00 second Generic expiration timer to LastCreatedUnit
        • Else - Actions
      • -------- --------
      • Custom script: call RemoveLocation(udg_Point_TowerRange)
 
Level 12
Joined
Jan 10, 2023
Messages
191
What are you using for a circle? Is it the model of the dummy?

That would make sense, I only doubted it because it looked like the circle I made (but I don't remember making an outline on mine) and that circle is a .blp not a model.

If the dummy is the circle, you should be able to use that simpler trigger, but set the sizes:
I put it at 100% X,Y, and Z because it didn't make sense to me, but if that was working for you before, then set them to
( 2 x Real_TotalTowerRange )% - X
( 2 x Real_TotalTowerRange )% - Y
( 100 )% - Z

If it was working before, it should work by setting those values.

If it's still not working, is there another trigger? (Seriously, dying to know lol)
It just really looks like some third trigger is drawing/placing an image and if so, that trigger will need to be altered for the variable changes as well.
 
Level 10
Joined
Jun 20, 2017
Messages
333
What are you using for a circle? Is it the model of the dummy?
A dummy unit.
Range Checker
[Solved] - Simple Circle

and that circle is a .blp not a model.
Can't use blp for a unit!

If the dummy is the circle, you should be able to use that simpler trigger, but set the sizes:
That's why I asked if there is a way to automatically adjust the size of the circle, without setting anything!
Guess not!
Even you used multiply on your trigger!
Custom script: set udg_Range_Image[ GetConvertedPlayerId( GetTriggerPlayer() ) ]= CreateImage( "war3mapImported\\Circle1024", 2*udg_Real, 2*udg_Real, 2*udg_Real, GetLocationX( udg_Points_Range[1] ), GetLocationY( udg_Points_Range[1] ), 0, udg_Real, udg_Real, 0, 3 )

Anyway, everything is working well for now, unless in the multiplayer!
 
Level 12
Joined
Jan 10, 2023
Messages
191
Even you used multiply on your trigger!

  • Custom script: set udg_Range_Image[ GetConvertedPlayerId( GetTriggerPlayer() ) ]= CreateImage( "war3mapImported\\Circle1024", 2*udg_Real, 2*udg_Real, 2*udg_Real, GetLocationX( udg_Points_Range[1] ), GetLocationY( udg_Points_Range[1] ), 0, udg_Real, udg_Real, 0, 3 )

There is an enormous difference between the multiplication in this equation and yours.

If I were to make mine more like yours, it would say something like:
  • Custom script: set udg_Range_Image[ GetConvertedPlayerId( GetTriggerPlayer() ) ]= CreateImage( "war3mapImported\\Circle1024",2*udg_Real*Apples, 2*udg_Real*Cinderella, 2*udg_Real*Blue, GetLocationX( udg_Points_Range[1] ), GetLocationY( udg_Points_Range[1] ), 0, udg_Real, udg_Real, 0, 3 )

My multiplication is relating the size of an image (size in distance units on the map) to the range of the unit (also a number of distance units on the map).

Your multiplication is relating model scale (completely unrelated to distance units) to the range of the unit (completely unrelated to model scale).


This is very good:
Anyway, everything is working well for now, unless in the multiplayer!
But I do hope you take the time to consider that the unit of measurement a variable represents (in this case distance units vs model scale) are a guide for which values should relate to which. This is going to cause you major problems down the road.

It's like adding 12 liters to 5 feet, or in our case multiplying 12 liters by 5 feet and wondering why that isn't a speed.
 
Status
Not open for further replies.
Top