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

Reworked Pick Destructables In Circle

Status
Not open for further replies.
Level 12
Joined
Jan 2, 2016
Messages
973
I checked the common.j, and the blizzard.j sites, and didn't find any function for this, so I made one myself.
It works like EnumDestructablesInCircleBJ, but it uses coordinates, instead of location.
JASS:
function EnumDestructablesInCircleFunc takes nothing returns boolean
    local real x = GetWidgetX(GetFilterDestructable()) - udg_X
    local real y = GetWidgetY(GetFilterDestructable()) - udg_Y
    return x*x + y*y <= bj_enumDestructableRadius
endfunction

function EnumDestructablesInCircle takes real x, real y, real radius, code userFunc returns nothing
    call SetRect(udg_EnumRect, x - radius, y - radius, x + radius, y + radius)
    set udg_X = x
    set udg_Y = y
    set bj_enumDestructableRadius = radius*radius
    call EnumDestructablesInRect(udg_EnumRect, Filter(function EnumDestructablesInCircleFunc), userFunc)
endfunction
Credit given to Bribe for the fully-optimized version.
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
The only reason I've seen used for picking destructibles is to kill them or open gates. This resource may be better transposed into a "kill valid desructables at coordinates" function which usesa dummy unit to validate the destructable.

The SetRect function exists so you don't ever need dynamic rects. MoveRectTo is also a thing.
 
Level 12
Joined
Jan 2, 2016
Messages
973
BPower, can you write the code here, because I don't exactly understand what you mean, by the way you describe it.

And yes, Bribe, I made this to kill and revive destructables (trees). But since I learned that I don't need to use locations - I thought it's a bad idea to start creating locations for my triggers, related to destructables, so I made this :p
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
You'll not need the square root calculation of you compare the distance to radius².
From a math aspect radius² equal radius*radius.

The point of interest should be defined by two non array variables.
posX = 10.
posY = 100.
^
instead of real array udg_coords.

You could directly return the result of your calculation and remove the if then endif condition.
return (x-udg_coords[0]*x-udg_coords[0])+(y-udg_coords[1]*y-udg_coords[1]) <= bj_enumDestructableRadius
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Should be done before the action function runs.
bj_enumDestructableRadius = radius*radius

The SetRect function exists so you don't ever need dynamic rects. MoveRectTo is also a thing.
I was thinking about that and basically you are right.
One constant rect and SetRect is the best solution in performance, code lenght, memory....

What I assume is that rects are together with locations one of the cheapest handles there is.
A rect is either 1 point and two vectors or 4 points.
The entire API is wrapped around this information by simple math. GetMax/Center/Min/Contains, EnumInRect, ...
SetRect and MoveRect just changes the point coordinates / vector lenght.

Rect(x1, y1, x2, x2) allocates a certain amount of memory and has to get a handle id during that process.
RemoveRect frees that memory and recycles the handle id.

That's all. Rects are not event, trigger or anywhere else associated.
Therefore I think that dynamically creating/destroying rects must be relative fast.
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Fully-optimized:

JASS:
function EnumDestructablesInCircleFunc takes nothing returns boolean
    local real x = GetWidgetX(GetFilterDestructable()) - udg_X
    local real y = GetWidgetY(GetFilterDestructable()) - udg_Y
    return x*x + y*y <= bj_enumDestructableRadius
endfunction

function EnumDestructablesInCircle takes real x, real y, real radius, code userFunc returns nothing
    call SetRect(udg_EnumRect, x - radius, y - radius, x + radius, y + radius)
    set udg_X = x
    set udg_Y = y
    set bj_enumDestructableRadius = radius*radius
    call EnumDestructablesInRect(udg_EnumRect, Filter(function EnumDestructablesInCircleFunc), userFunc)
endfunction

Keep in mind that bj_isUnitGroupInRectRect is not set to a static rect, so you cannot use it for this system. The rect EnumRect must be created in variable editor and during an initialization function set to a rect of any size.

This is the vJass form, which is more recommended given that the resource is only going to get bigger from this point:

JASS:
library EnumDestructablesInCircle initializer Init

globals
    private rect r = null
    private real sx = 0.00
    private real sy = 0.00
    private real rad = 0.00
    private boolexpr bxpr = null
endglobals

private function Enum takes nothing returns boolean
    local real x = GetWidgetX(GetFilterDestructable()) - sx
    local real y = GetWidgetY(GetFilterDestructable()) - sy
    return x*x + y*y <= rad
endfunction

function EnumDestructablesInCircle takes real x, real y, real radius, code userFunc returns nothing
    call SetRect(r, x - radius, y - radius, x + radius, y + radius)
    set sx = x
    set sx = y
    set rad = radius*radius
    call EnumDestructablesInRect(r, bxpr, userFunc)
endfunction

private function Init takes nothing returns nothing
    set r = Rect(0.00, 0.00, 32.00, 32.00)
    set bxpr = Filter(function Enum)
endfunction

endlibrary
 
Level 12
Joined
Jan 2, 2016
Messages
973
JASS:
function IsUnitGroupInRectBJEnum takes nothing returns nothing
    if not RectContainsUnit(bj_isUnitGroupInRectRect, GetEnumUnit()) then
        set bj_isUnitGroupInRectResult = false
    endif
endfunction

//===========================================================================
// Returns true if every unit of the group is within the given rect.
//
function IsUnitGroupInRectBJ takes group g, rect r returns boolean
    set bj_isUnitGroupInRectResult = true
    set bj_isUnitGroupInRectRect = r
    call ForGroup(g, function IsUnitGroupInRectBJEnum)
    return bj_isUnitGroupInRectResult
endfunction
This is the only blizzard.j function I could find, that's using this rect variable.

EDIT: Actually, after some thinking, I realized what may go wrong - if a player uses the BJ function - the rect will be set to some region (let's say a constant region "Region 1"), and after using my function - that region will get set to something else, and "Region 1" will not be itself anymore :p
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
This is the only blizzard.j function I could find, that's using this rect variable.
I don't see why can't the 2 functions work together, considering they can't possibly run simutaneously.
Because bj_isUnitGroupInRectRect is not static,
it points to the last rect passed in IsUnitGroupInRectBJ, more likely to null.

Bribe already mentioned it above:
Quote: Keep in mind that bj_isUnitGroupInRectRect is not set to a static rect, so you cannot use it for this system.
 
Level 12
Joined
Jan 2, 2016
Messages
973
Okay, now I have a problem:
JASS:
function EnumDestructablesInCircle takes real x, real y, real radius, code actionFunc returns nothing
    call SetRect(udg_Temp_Region,x-radius,y-radius,x+radius,y+radius)
    //local rect r = Rect(x-radius,y-radius,x+radius,y+radius)
    set udg_X = x
    set udg_Y = y
    set bj_enumDestructableRadius = radius*radius
    call EnumDestructablesInRect(udg_Temp_Region , Filter(function IsDestructableInCircle), actionFunc )
    //call EnumDestructablesInRect(r , Filter(function IsDestructableInCircle), actionFunc )
    //call RemoveRect(r)
endfunction
When I switch the functions - it works, but with the functions as they are now - it doesn't.... what's wrong?

And what's the difference between "Region" and "Rect" btw? I see that the common.j has some "CreateRegion" functions as well.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
JASS:
function EnumDestructablesInCircleFunc takes nothing returns boolean
    local real x = GetWidgetX(GetFilterDestructable()) - udg_X
    local real y = GetWidgetY(GetFilterDestructable()) - udg_Y
    return x*x + y*y <= bj_enumDestructableRadius
endfunction

function EnumDestructablesInCircle takes real x, real y, real radius, code userFunc returns nothing
    if udg_EnumRect == null then
        set udg_EnumRect = Rect(x - radius, y - radius, x + radius, y + radius)
    else
        call SetRect(udg_EnumRect, x - radius, y - radius, x + radius, y + radius)
    endif
    set udg_X = x
    set udg_Y = y
    set bj_enumDestructableRadius = radius*radius
    call EnumDestructablesInRect(udg_EnumRect, Filter(function EnumDestructablesInCircleFunc), userFunc)
endfunction
 
Level 12
Joined
Jan 2, 2016
Messages
973
JASS:
function IsDestructableInCircle takes nothing returns boolean
    local real x = GetDestructableX(GetFilterDestructable()) - udg_X
    local real y = GetDestructableY(GetFilterDestructable()) - udg_Y
    return x*x+y*y <= bj_enumDestructableRadius
endfunction

function EnumDestructablesInCircle takes real x, real y, real radius, code actionFunc returns nothing
    if udg_Temp_Region == null then
        set udg_Temp_Region = Rect(x-radius,y-radius,x+radius,y+radius)
    else
        call SetRect(udg_Temp_Region,x-radius,y-radius,x+radius,y+radius)
    endif
//    local rect r = Rect(x-radius,y-radius,x+radius,y+radius)
    set udg_X = x
    set udg_Y = y
    set bj_enumDestructableRadius = radius*radius
    call EnumDestructablesInRect(udg_Temp_Region , Filter(function IsDestructableInCircle), actionFunc )
//    call EnumDestructablesInRect(r , Filter(function IsDestructableInCircle), actionFunc )
//    call RemoveRect(r)
endfunction
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
The code Bribe posted works perfect, so does yours.
GetWidgetX/Y and GetDestructableX/Y are equivalent for destructable objects.
destructable extends widget, hence widget API can be used on destructables objects.

If it is not working for you, then because your action function ( actionFunc ) is not using
GetEnumDestructable().

GetFilterDestructable() is valid in a filter function.
GetEnumDestructable() is valid in a action function.
 
Level 12
Joined
Jan 2, 2016
Messages
973
Try it..
This function is in the map's header.
I use it with the sliding - destroying trees, that are in the way of the sliding (I use it in 1 more trigger).
You can test it like:
1) start the map
2) write "-test"
3) write "-build"
4) summon the only available hero in the altar
5) learn the 3-rd skill
6) go close to trees
7) write "-kill" while you have the hero selected

note: the sliding angle is from the middle of the map towards the hero (when you use the "-kill" command)
note2: you can write "-lvl 25" or at least "-lvl 7" to get the 3-rd skill maxed for less cooldown.
note3: currently the "working" functions are used, you need to uncoment the other ones, and make the current ones to comments.

It destroyes trees when I'm using "local rect r", but it doesn't work when I'm using the "udg_Temp_Region".

The other way you can test it is by:
~steps from 1 to 3 are the same~
4) summon the Elder Druid (from the Druid Totem - using the Chimera's default building model)
5) summon a balista (from the Burrow Den building)
6) destroy some trees with the balista
7) attempt to revive them with the Druid

note: you need to research the balista's upgrade for it to be able to attack the trees. You can write "-upgrade all" if you don't want to wait for it to be researched.
 

Attachments

  • New Era BETA v0.1.w3x
    494.1 KB · Views: 40
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
I used Variable Manager to look up Temp_Region, and yes, this is why you want to use PROPER, isolated variable names. Even better when you use vJass so you can use private variables and never run into the problem which you have now.

The issue:

  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • (Entered chat string) Equal to -build
    • Then - Actions
      • Set Temp_Player = (Triggering player)
      • Set Temp_Point = (Point(5600.00, -7850.00))
      • Set TempPoint = (Point(8700.00, -10000.00))
      • Set Temp_Region = (Region(Temp_Point, TempPoint))
      • Custom script: call RemoveLocation (udg_Temp_Point)
      • Custom script: call RemoveLocation (udg_TempPoint)
      • Custom script: set bj_wantDestroyGroup = true
      • Unit Group - Pick every unit in (Units in Temp_Region) and do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Unit-type of (Picked unit)) Equal to Gold Mine
            • Then - Actions
            • Else - Actions
              • Unit - Remove (Picked unit) from the game
      • Custom script: call RemoveRect(udg_Temp_Region)
You removed Temp_Region without setting it to null. This means that in your custom script map header which, conflictingly, uses the same variable, you will never be able to use Temp_Region as it is removed.
 
Level 12
Joined
Jan 2, 2016
Messages
973
Yeah.... I just discovered that too... Started reworking my "test commands" trigger to be in JASS, and figured that out. Came to the forum to say I've found out why wasn't it working, and I saw your post :D

I'm not sure if I'm using it in any other triggers.. need to check.. I will make most of the triggers in JASS, so hopefully I wouldn't miss anything.
 
Status
Not open for further replies.
Top