1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  3. The 15th Mini-Mapping Contest came to an end. The Secrets of Warcraft 3 are soon to be revealed! Come and vote in the public poll for your favorite maps.
    Dismiss Notice
  4. The 12th incarnation of the Music Contest is LIVE! The theme is Synthwave. Knight Rider needs a song to listen to on his journey. You should definitely have some fun with this theme!
    Dismiss Notice
  5. Join other hivers in a friendly concept-art contest. The contestants have to create a genie coming out of its container. We wish you the best of luck!
    Dismiss Notice
  6. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Line Segment Enumeration .v2.1a

Submitted by IcemanBo
This bundle is marked as approved. It works and satisfies the submission rules.
Info

Allows to enumerate widgets inside a line segment with an offset.
So basicly it will result in a rect, but which is also allowed to be rotated.

There is normal JASS, and also a GUI version.
For GUI, you should open the demo map, to see the triggers.

Code

Code (vJASS):

library LineSegmentEnumeration /* v2.1a -- hiveworkshop.com/threads/line-segment-enumeration-v1-1.286552/

    Information
    ¯¯¯¯¯¯¯¯¯¯¯

        Allows to enumerate widgets inside a line segment with an offset.
        So basicly it will result in a rect, but which is also allowed to be rotated.
       
       
    Mechanics
    ¯¯¯¯¯¯¯¯¯
       
        (Issue:)
       
        The problem with normal jass rects is that they aren't defined by 4 points, but only by 4 values: x/y -min/max.
        The result is that a jass rect is never rotated, so it's always paralel to the x/y axis.
       
        But when we draw a line from point A to point B we might also create a non-axix-parelel rect, and then
        we can't use the normal rect natives from jass anymore to find out if a point is inside the rect.
       
        (Solution:)
       
        To solve this problem the system does following:
       
        jass rect: rectangular defined by 4 values (axis paralel)
        custom rect: the real rectangular that is defined by user (not axis parelel)
       
        1. Create a big jass rect that is big enough so we can ensure to enum all widgets that are potentialy inside our custom rect. (Enum_Group)
           This Enum_Group will contain all wanted units, but may also contain not wanted units.
           
        2. We rotate the custom rect around Point B, by angle "theta", so it gets now a normal, non-rotated rect. (axis parelel)
       
        3. We loop through Enum_Group, and rotate all widget's coordinates also by the same "theta" value around Point B.
       
        4. Now what happened is that the custom rect was manipulated so it got axis parelel. And we also used the same maths
           to manipulate the x/y coordinate of our widgets, so the relation between the custom rect and the widget is still the very same.
           
           But what we can do now is to use native jass logics to check if the widget is within the bounds of the rotated custom rect
           with only comparing it's x/y coordinates with the rect's x/y -min/max coodinates.
           
        Note: this approach is not the most performant one, but it doesn't work with custom types. So if you already work with for example
        struct rects or other polygons, and you care for trigomnomig orientation you can write straight forward functions to check if a point
        is inside a quadrilateral, as standalone function.

*/

//  --- API ---
//! novjass

    struct LineSegment
   
        static method EnumUnits takes group whichgroup, real ax, real ay, real bx, real by, real offset returns nothing
   
        static method EnumDestructables takes real ax, real ay, real bx, real by, real offset returns nothing
           
        //  after enumerated destructables you have access to:
       
            static integer DestructableCounter      // starts with index "0"
            static destructable array Destructable
           
        static method EnumItems takes real ax, real ay, real bx, real by, real offset returns nothing
           
        //  after enumerated items you have access to:
       
            static integer ItemCounter      // starts with index "0"
            static destructable array Item
           
//! endnovjass
// ==== End API ====

struct LineSegment extends array

    private static constant rect RECT = Rect(0, 0, 0, 0)
   
    private static constant group GROUP = CreateGroup()
   
    public static method EnumUnits takes group whichgroup, real ax, real ay, real bx, real by, real offset returns nothing
        local real angle = Atan2(by - ay, bx - ax)
        local real theta = ModuloReal(angle, bj_PI/2)
        local real thetaSin
        local real thetaCos
        local unit u
        local real x
        local real y
       
        local real x_new
        local real y_new
       
        local real maxX
        local real maxY
        local real minX
        local real minY
       
        local real x1
        local real y1
        local real x2
        local real y2
        local real x3
        local real y3
        local real x4
        local real y4
       
        // Enum all potential units
        if ax > bx then
            set maxX = ax + offset
            set minX = bx - offset
        else
            set maxX = bx + offset
            set minX = ax - offset
        endif
       
        if ay > by then
            set maxY = ay + offset
            set minY = by - offset
        else
            set maxY = by + offset
            set minY = ay - offset
        endif
        call SetRect(RECT, minX, minY, maxX, maxY)
        call GroupEnumUnitsInRect(GROUP, RECT, null)
       
        // Create and rotate rect around point B, so it gets unrotated
        set angle = angle - theta
        set angle = angle*-1
        set thetaSin = Sin(-theta)
        set thetaCos = Cos(-theta)
       
        set x_new = (ax-bx)*thetaCos - (ay-by)*thetaSin + bx
        set y_new = (ax-bx)*thetaSin + (ay-by)*thetaCos + by
       
        set x1 = x_new + offset*Cos(angle - bj_PI*0.5)
        set y1 = y_new + offset*Sin(angle - bj_PI*0.5)
        set x2 = x_new + offset*Cos(angle + bj_PI*0.5)
        set y2 = y_new + offset*Sin(angle + bj_PI*0.5)
        set x3 = bx + offset*Cos(angle - bj_PI*0.5)
        set y3 = by + offset*Sin(angle - bj_PI*0.5)
        set x4 = bx + offset*Cos(angle + bj_PI*0.5)
        set y4 = by + offset*Sin(angle + bj_PI*0.5)
       
        // define max/min values of the new non-rotated rect
       
        if x1 > x2 then
            set maxX = RMaxBJ(x1, x3)
            set minX = RMinBJ(x2, x4)
        else
            set maxX = RMaxBJ(x2, x4)
            set minX = RMinBJ(x1, x3)
        endif
       
        if y1 > y2 then
            set maxY = RMaxBJ(y1, y3)
            set minY = RMinBJ(y2, y4)
        else
            set maxY = RMaxBJ(y2, y4)
            set minY = RMinBJ(y1, y3)
        endif
       
        // enum through all tracked units, rotate it around B, and check if it's inside bounds
       
        call GroupClear(whichgroup)
        loop
            set u = FirstOfGroup(GROUP)
            exitwhen u == null
           
            set x = GetUnitX(u)
            set y = GetUnitY(u)
           
            set x_new = (x-bx)*thetaCos - (y-by)*thetaSin + bx
            set y_new = (x-bx)*thetaSin + (y-by)*thetaCos + by
           
            if x_new >= minX and x_new <= maxX and y_new >= minY and y_new <= maxY then
                call GroupAddUnit(whichgroup, u)
            endif
           
            call GroupRemoveUnit(GROUP, u)
        endloop
    endmethod
   
//! textmacro LSE_WIDGET takes TYPE, NAME
    private static integer $NAME$Counter_p = -1
    private static $TYPE$ array $NAME$_p
    public static integer $NAME$Counter = -1
    public static $TYPE$ array $NAME$
   
    private static method on$NAME$Filter takes nothing returns nothing
        set $NAME$Counter_p = $NAME$Counter_p + 1
        set $NAME$_p[$NAME$Counter_p] = GetEnum$NAME$()
    endmethod
   
    public static method Enum$NAME$s takes real ax, real ay, real bx, real by, real offset returns nothing
        local real angle = Atan2(by - ay, bx - ax)
        local real theta = ModuloReal(angle, bj_PI/2)
        local real thetaSin
        local real thetaCos
        local $TYPE$ t
        local real x
        local real y
       
        local real x_new
        local real y_new
       
        local real maxX
        local real maxY
        local real minX
        local real minY
       
        local real x1
        local real y1
        local real x2
        local real y2
        local real x3
        local real y3
        local real x4
        local real y4
       
        // Enum all potential widgets
        if ax > bx then
            set maxX = ax + offset
            set minX = bx - offset
        else
            set maxX = bx + offset
            set minX = ax - offset
        endif
       
        if ay > by then
            set maxY = ay + offset
            set minY = by - offset
        else
            set maxY = by + offset
            set minY = ay - offset
        endif
        call SetRect(RECT, minX, minY, maxX, maxY)
        set $NAME$Counter_p = -1
        call Enum$NAME$sInRect(RECT, null, function thistype.on$NAME$Filter)
       
        // Create and rotate rect around point B, so it gets unrotated
        set angle = angle - theta
        set angle = angle*-1
        set thetaSin = Sin(-theta)
        set thetaCos = Cos(-theta)
       
        set x_new = (ax-bx)*thetaCos - (ay-by)*thetaSin + bx
        set y_new = (ax-bx)*thetaSin + (ay-by)*thetaCos + by
       
        set x1 = x_new + offset*Cos(angle - bj_PI*0.5)
        set y1 = y_new + offset*Sin(angle - bj_PI*0.5)
        set x2 = x_new + offset*Cos(angle + bj_PI*0.5)
        set y2 = y_new + offset*Sin(angle + bj_PI*0.5)
        set x3 = bx + offset*Cos(angle - bj_PI*0.5)
        set y3 = by + offset*Sin(angle - bj_PI*0.5)
        set x4 = bx + offset*Cos(angle + bj_PI*0.5)
        set y4 = by + offset*Sin(angle + bj_PI*0.5)
       
        // define max/min values of the new non-rotated rect
       
        if x1 > x2 then
            set maxX = RMaxBJ(x1, x3)
            set minX = RMinBJ(x2, x4)
        else
            set maxX = RMaxBJ(x2, x4)
            set minX = RMinBJ(x1, x3)
        endif
       
        if y1 > y2 then
            set maxY = RMaxBJ(y1, y3)
            set minY = RMinBJ(y2, y4)
        else
            set maxY = RMaxBJ(y2, y4)
            set minY = RMinBJ(y1, y3)
        endif
       
         // enum through all tracked widgets, rotate it around B, and check if it's inside bounds
       
        set $NAME$Counter = -1
        loop
            exitwhen $NAME$Counter_p < 0
            set t = $NAME$_p[$NAME$Counter_p]
           
            set x = Get$NAME$X(t)
            set y = Get$NAME$Y(t)
               
            set x_new = (x-bx)*thetaCos - (y-by)*thetaSin + bx
            set y_new = (x-bx)*thetaSin + (y-by)*thetaCos + by
               
            if x_new >= minX and x_new <= maxX and y_new >= minY and y_new <= maxY then
               
                set $NAME$Counter = $NAME$Counter + 1
                set $NAME$[$NAME$Counter] = t
            endif
           
            set $NAME$_p[$NAME$Counter_p] = null
            set $NAME$Counter_p = $NAME$Counter_p - 1
        endloop
    endmethod
//! endtextmacro

//! runtextmacro LSE_WIDGET("destructable", "Destructable")
//! runtextmacro LSE_WIDGET("item", "Item")
   
endstruct
endlibrary
 


Code (vJASS):

library LineSegmentEnumerationGUI uses LineSegmentEnumeration /* v2.0
*/

    private struct LineSegmentGUI extends array
 
        private static method GetUnits takes nothing returns nothing
            call LineSegment.EnumUnits(udg_LSE_Group, GetLocationX(udg_LSE_Loc_1), GetLocationY(udg_LSE_Loc_1), GetLocationX(udg_LSE_Loc_2), GetLocationY(udg_LSE_Loc_2), udg_LSE_Offset)
        endmethod
     
        private static method GetDestructables takes nothing returns nothing
            call LineSegment.EnumDestructables(GetLocationX(udg_LSE_Loc_1), GetLocationY(udg_LSE_Loc_1), GetLocationX(udg_LSE_Loc_2), GetLocationY(udg_LSE_Loc_2), udg_LSE_Offset)
            set udg_LSE_DestructableCounter = -1
            loop
                exitwhen LineSegment.DestructableCounter < 0
                set udg_LSE_DestructableCounter = udg_LSE_DestructableCounter + 1
                set udg_LSE_Destructable[udg_LSE_DestructableCounter] = LineSegment.Destructable[LineSegment.DestructableCounter]
                set LineSegment.DestructableCounter = LineSegment.DestructableCounter - 1
            endloop
        endmethod
     
        private static method GetItems takes nothing returns nothing
            call LineSegment.EnumItems(GetLocationX(udg_LSE_Loc_1), GetLocationY(udg_LSE_Loc_1), GetLocationX(udg_LSE_Loc_2), GetLocationY(udg_LSE_Loc_2), udg_LSE_Offset)
            set udg_LSE_ItemCounter = -1
            loop
                exitwhen LineSegment.ItemCounter < 0
                set udg_LSE_ItemCounter = udg_LSE_ItemCounter + 1
                set udg_LSE_Item[udg_LSE_ItemCounter] = LineSegment.Item[LineSegment.ItemCounter]
                set LineSegment.ItemCounter = LineSegment.ItemCounter - 1
            endloop
        endmethod

        private static method onInit takes nothing returns nothing
            set udg_LSE_GetUnits = CreateTrigger()
            set udg_LSE_GetDestructables = CreateTrigger()
            set udg_LSE_GetItems = CreateTrigger()
            call TriggerAddAction(udg_LSE_GetUnits, function thistype.GetUnits)
            call TriggerAddAction(udg_LSE_GetDestructables, function thistype.GetDestructables)
            call TriggerAddAction(udg_LSE_GetItems, function thistype.GetItems)
        endmethod
     
    endstruct

endlibrary
 



How to use

  • LSE GUI How to Use
    • Ereignisse
    • Bedingungen
    • Aktionen
      • -------- ----------------------------------------------------------- --------
      • -------- Step 1 - Define endpoints and offset tolerance of line. --------
      • -------- ----------------------------------------------------------- --------
      • Set LSE_Loc_1 = (Random point in (Playable map area))
      • Set LSE_Loc_2 = (Random point in (Playable map area))
      • Set LSE_Offset = 100.00
      • -------- ----------------------------------------------------------- --------
      • -------- Step 2 Enumeration --------
      • -------- ----------------------------------------------------------- --------
      • -------- 2.1 Units --------
      • -------- ----------------------------------------------------------- --------
      • Trigger - Run LSE_GetUnits (ignoring conditions)
      • -------- ----------------------------------------------------------- --------
      • -------- Now you can work with LSE_group. --------
      • -------- ----------------------------------------------------------- --------
      • Unit group - Pick every unit in LSE_Group and do (Actions)
        • Loop - Actions
          • -------- ----------------------------------------------------------- --------
          • -------- Do some actions here for example. --------
          • -------- ----------------------------------------------------------- --------
      • -------- ----------------------------------------------------------- --------
      • -------- Attention -- do never destroy LSE_Group. --------
      • -------- ----------------------------------------------------------- --------
      • -------- 2.2 Destructables --------
      • -------- ----------------------------------------------------------- --------
      • Trigger - Run LSE_GetDestructables (ignoring conditions)
      • -------- ----------------------------------------------------------- --------
      • -------- Loop from 0 to LSE_DestructableCounter --------
      • -------- ----------------------------------------------------------- --------
      • For each (Integer A) from 0 to LSE_DestructableCounter, do (Actions)
        • Loop - Actions
          • -------- ----------------------------------------------------------- --------
          • -------- Use LSE_Destructable[Integer A] to refer to current destructable. --------
          • -------- ----------------------------------------------------------- --------
      • -------- ----------------------------------------------------------- --------
      • -------- 2.3 Items --------
      • -------- ----------------------------------------------------------- --------
      • Trigger - Run LSE_GetItems (ignoring conditions)
      • -------- ----------------------------------------------------------- --------
      • -------- Loop from 0 to LSE_ItemCounter --------
      • -------- ----------------------------------------------------------- --------
      • For each (Integer A) from 0 to LSE_ItemCounter, do (Actions)
        • Loop - Actions
          • -------- ----------------------------------------------------------- --------
          • -------- Use LSE_Item[Integer A] to refer to current item. --------
          • -------- ----------------------------------------------------------- --------
      • -------- ----------------------------------------------------------- --------
      • -------- To prevent leaks. --------
      • -------- ----------------------------------------------------------- --------
      • Custom script: call RemoveLocation(udg_LSE_Loc_1)
      • Custom script: call RemoveLocation(udg_LSE_Loc_2)



How To Import A Spell/System
Previews
Contents

Line Segment Enumeration .v1.1 (Map)

Line Segment Enumeration .v2.1a (Map)

Reviews
KILLCIDE
A simple mechanic, yet the math behind it all is beyond my scope of knowledge! A great thank you to Ammorth and Anitarf for the small work such as this they do. We can of course cannot forget to thank the IcemanBo for applying it into a GUI-Friendly...
  1. KILLCIDE

    KILLCIDE

    Administrator

    Joined:
    Jul 22, 2015
    Messages:
    3,491
    Resources:
    20
    Models:
    2
    Icons:
    10
    Spells:
    7
    Tutorials:
    1
    Resources:
    20
    A simple mechanic, yet the math behind it all is beyond my scope of knowledge! A great thank you to Ammorth and Anitarf for the small work such as this they do.

    We can of course cannot forget to thank the IcemanBo for applying it into a GUI-Friendly and vanilla editor compatible system with extremely easy to use API. The code looks good as usual, and the demo map is superb (; Personally, I would use another global group instead of declaring
    local group g
    all the time.

    Approved with a 4/5 rating. I hope to see future submissions using this library.
     
  2. jakeZinc

    jakeZinc

    Joined:
    Aug 13, 2013
    Messages:
    1,366
    Resources:
    20
    Spells:
    20
    Resources:
    20
    Very useful system especially when accompanied for lightnings!

    Well expect me to create a future spell using this stuff ^^.
     
  3. Aniki

    Aniki

    Joined:
    Nov 7, 2014
    Messages:
    517
    Resources:
    4
    Spells:
    1
    JASS:
    3
    Resources:
    4
    Have you tried:
    • Actions
      • -------- not important, just for visuals --------
      • Custom script: call MoveLightningEx(udg_Lightning, true, GetUnitX(udg_Unit1), GetUnitY(udg_Unit1), 75.00, GetUnitX(udg_Unit2), GetUnitY(udg_Unit2), 75.00)
      • -------- --------
      • -------- IMPORTANT STUFF --------
      • Set LSE_Loc_1 = (Position of Unit1)
      • Set LSE_Loc_2 = (Position of Unit2)
      • Set LSE_Width = 500.00


    i.e setting udg_LSE_Width to 500 for example and move a knight towards a chicken, the chicken dies when the knight is 500 units or less away from it.

    Another example would be if we use the
    LSE_GroupEnumUnitsInRangeOfSegment
    function to get all the units between a caster unit and a target location say 600.0 away (when casting a point target spell), what would happen is that units that are behind the caster and less than 600.0 units away would also be picked (also units that are 600.0 away from the target point in the direction that the caster is facing would also be picked).
     
  4. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,107
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    Definitly true. I've also chosen a wrong name. It's actually the max distance from the line. And "width" implies total width, so max wrong of 2x amount.:( ^^
    Thanks, will fix tomorrow!
     
  5. Aniki

    Aniki

    Joined:
    Nov 7, 2014
    Messages:
    517
    Resources:
    4
    Spells:
    1
    JASS:
    3
    Resources:
    4
    vJass version:

    Code (vJASS):

    library EnumInRangeOfLineSegment

    globals
        private constant real max_collision_size = 48.0
    endglobals


    static if DEBUG_MODE then
    private function panic takes string s returns nothing
        call BJDebugMsg("|cffFF0000" + s + "|r")
        if 1 / 0 == 1 then
        endif
    endfunction
    endif

    globals
        private group gg = CreateGroup()
    endglobals
    function GroupEnumUnitsInRangeOfLineSegment takes group g, real ax, real ay, real bx, real by, real line_half_width returns nothing
        local real abx
        local real aby
        local real ab_len_sq
        local real radius
        local real t
        local unit u

    static if DEBUG_MODE then
        if ax == bx and ay == by then
            call panic("[GroupEnumUnitsInRangeOfLineSegment] error: " + "expected a line segment, got point (" + R2S(ax) + ", " + R2S(ay) + ")")
        endif
    endif

        set abx = bx - ax
        set aby = by - ay
        set ab_len_sq = abx*abx + aby*aby

        set radius = 0.5 * SquareRoot(abx*abx + aby*aby) + line_half_width + max_collision_size
        call GroupEnumUnitsInRange(gg, ax + (0.5 * abx), ay + (0.5 * aby), radius, null)
        loop
            set u = FirstOfGroup(gg)
            exitwhen u == null
            call GroupRemoveUnit(gg, u)

            set t = ((GetUnitX(u) - ax)*abx + (GetUnitY(u) - ay)*aby) / ab_len_sq
            if 0.0 <= t and t <= 1.0 and IsUnitInRangeXY(u, ax + t*abx, ay + t*aby, line_half_width) then
                call GroupAddUnit(g, u)
            endif
        endloop
    endfunction

    globals
        destructable array ls_destructables // 1 based array
        integer ls_destructables_count = 0

        private constant real tau = 2.0 * (355.0 / 113.0)
        private rect rr = Rect(0.0, 0.0, 0.0, 0.0)
        private real pax
        private real pay
        private real pbx
        private real pby
        private real pline_half_width
    endglobals

    private function enum_destructables takes nothing returns nothing
        local destructable d = GetEnumDestructable()
        local real dx = GetWidgetX(d)
        local real dy = GetWidgetY(d)
        local real abx = pbx - pax
        local real aby = pby - pay
        local real ab_len_sq = abx*abx + aby*aby
        local real t = ((dx - pax)*abx + (dy - pay)*aby) / ab_len_sq
        local real px
        local real py
        local real dist_sq

        if 0.0 <= t and t <= 1.0 then
            set px = pax + t*abx
            set py = pay + t*aby
            set dist_sq = (dx - px)*(dx - px) + (dy - py)*(dy - py)
            if dist_sq <= pline_half_width*pline_half_width then
                set ls_destructables_count = ls_destructables_count + 1
                set ls_destructables[ls_destructables_count] = d
            endif
        endif

        set d = null
    endfunction

    function EnumDestructablesInRangeOfLineSegment takes real ax, real ay, real bx, real by, real line_half_width returns nothing
        local real hw
        local real minx
        local real miny
        local real maxx
        local real maxy
        local real d
        local real ang
        local integer quadrant

    static if DEBUG_MODE then
        if ax == bx and ay == by then
            call panic("[EnumDestructablesInRangeOfLineSegment] error: " + "expected a line segment, got point (" + R2S(ax) + ", " + R2S(ay) + ")")
        endif
    endif

        loop
            exitwhen ls_destructables_count <= 0
            set ls_destructables[ls_destructables_count] = null
            set ls_destructables_count = ls_destructables_count - 1
        endloop

        set hw = line_half_width
        set d = Atan2(by - ay, bx - ax)

        set ang = d
        if ang < 0.0 then
            set ang = ang + tau
        endif
        set quadrant = R2I(ang / (tau/4.0))

        // there's probably an easier way to find the 2 points but I don't know it =)
        if quadrant == 0 then
            set minx = ax + hw * Cos(d + tau/4.0)
            set miny = ay + hw * Sin(d - tau/4.0)
            set maxx = bx + hw * Cos(d - tau/4.0)
            set maxy = by + hw * Sin(d + tau/4.0)

        elseif quadrant == 1 then
            set minx = bx + hw * Cos(d + tau/4.0)
            set miny = ay + hw * Sin(d + tau/4.0)
            set maxx = ax + hw * Cos(d - tau/4.0)
            set maxy = by + hw * Sin(d - tau/4.0)

        elseif quadrant == 2 then
            set minx = bx + hw * Cos(d - tau/4.0)
            set miny = by + hw * Sin(d + tau/4.0)
            set maxx = ax + hw * Cos(d + tau/4.0)
            set maxy = ay + hw * Sin(d - tau/4.0)

        else // if quadrant == 3 then
            set minx = ax + hw * Cos(d - tau/4.0)
            set miny = by + hw * Sin(d - tau/4.0)
            set maxx = bx + hw * Cos(d + tau/4.0)
            set maxy = ay + hw * Sin(d + tau/4.0)

        endif
        call SetRect(rr, minx, miny, maxx, maxy)

        set pax = ax
        set pay = ay
        set pbx = bx
        set pby = by
        set pline_half_width = hw
        call EnumDestructablesInRect(rr, null, function enum_destructables)
    endfunction

    endlibrary

     


    Edit: changed this:
    Code (vJASS):

        set hw = line_half_width
        set minx = RMinBJ(ax, bx) - hw
        set miny = RMinBJ(ay, by) - hw
        set maxx = RMaxBJ(ax, bx) + hw
        set maxy = RMaxBJ(ay, by) + hw
        call SetRect(rr, minx, miny, maxx, maxy)
     


    to this:
    Code (vJASS):

        set hw = line_half_width
        set d = Atan2(by - ay, bx - ax)
        set minx = ax + hw * Cos(d + tau/4.0)
        set miny = ay + hw * Sin(d - tau/4.0)
        set maxx = bx + hw * Cos(d - tau/4.0)
        set maxy = by + hw * Sin(d + tau/4.0)
        call SetRect(rr, minx, miny, maxx, maxy)
     


    which I think is a better fit.

    Edit2: now should work for all quadrants =)
     
    Last edited: Jan 17, 2017
  6. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,255
    Resources:
    6
    Models:
    1
    Icons:
    1
    Spells:
    3
    JASS:
    1
    Resources:
    6
    @IcemanBo,

    Can I add a sample spell to show how the system works?
     
  7. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,107
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    It's a bit flawed atm, pointed out by Aniki, but sure.
     
  8. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,255
    Resources:
    6
    Models:
    1
    Icons:
    1
    Spells:
    3
    JASS:
    1
    Resources:
    6
    Code (vJASS):

    //Written in pseudo vJass
    //Thanks to IcemanBo for the LSE system

    //Globals section is a vjass feature, since it uses a preprocessor to generate these variables.

    globals
        constant hashtable SPELL_HASH = InitHashtable()
        unit enum_e
    endglobals

    constant function Radius takes nothing returns real
        return 45.
    endfunction

    function Timer_Move_Unit_Actions takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local real remaining = 1 - LoadReal(SPELL_HASH, GetHandleId(t), 1)
        local unit caster = LoadUnitHandle(SPELL_HASH, GetHandleId(t), 0)
        local real getFacing = LoadReal(SPELL_HASH, GetHandleId(t), 2)
        local real c_x = GetUnitX(caster)
        local real c_y = GetUnitY(caster)
        local group ggwp = CreateGroup()
        local group getGroup = LoadGroupHandle(SPELL_HASH, GetHandleId(t), 3)
        //Initializing our variables retrieved from the timer that expired.
        if remaining > 0 then
            call LSE_GroupEnumUnitsInRangeOfSegment(ggwp, c_x, c_y, c_x + Radius() * Cos(getFacing), c_y + Radius() * Sin(getFacing), Radius())
            //call LSE_GroupEnumUnitsInRangeOfSegment(udg_LSE_Group, c_x, c_y, c_x + Radius() * Cos(getFacing), c_y + Radius() * Sin(getFacing), Radius())
            //Normally, this would return a usable global variable, but it is too late to incorporate it now.
            loop
                set enum_e = FirstOfGroup(ggwp)
                exitwhen enum_e == null
                if not IsUnitInGroup(enum_e, getGroup) and IsUnitEnemy(enum_e, GetOwningPlayer(caster)) and GetWidgetLife(enum_e) > 0.405 then
                    //This checks whether or not the unit was previously enumerated and included.
                    //The GetWidgetLife native gets the life of the unit and checks  whether or not its' health is greater than 0.405.
                    //If it is, it will proceed to the next line since it is alive.
                    call UnitDamageTarget(caster, enum_e, 75, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, null)
                    //action here
                    call GroupAddUnit(getGroup, enum_e)
                    //Includes the enumerated unit if it hasn't been included already.
                endif
                call GroupRemoveUnit(ggwp, enum_e)
                //FirstOfGroup loops use up all of the units in a group, remmoving them so as not to cause an infinite loop.
            endloop
            call SetUnitX(caster, c_x + Radius() * Cos(getFacing))
            call SetUnitY(caster, c_y + Radius() * Sin(getFacing))
            //call SetUnitPositionLoc()
            //Think of this as SetUnitPositionLoc without the location handle, which means that you don't need to call RemoveLocation()
            //with the benefit of not needing to null the real values.
            call SaveReal(SPELL_HASH, GetHandleId(t), 1, LoadReal(SPELL_HASH, GetHandleId(t), 1) + 0.03125)
            //This updates the passage of time so as not to cause an infinite loop.
            //Q: How would this create an infinite loop?
            //A: The timer will always repeat itself, not stopping until it is manually paused.
            //  This creates a problem for us, since we can't access it directly but through
            //  this function only. Thus, we include this variable to account for the passage of
            //  time.
            //
            //     Going back to the if-then statement, the statement remaining > 0 checks
            //  if the time elapsed is not yet 1 since the variable in question is stated as:
            //  remaining = 1 - LoadReal(SPELL_HASH, GetHandleId(t), 1)
            //  If it is false, it then proceeds to the else portion of the code.
        else
            call FlushChildHashtable(SPELL_HASH, GetHandleId(t))
            //This removes all data associated with the expiring timer
            call PauseTimer(t)
            call DestroyTimer(t)
            //Why do we pause the timer? It could be that the timer is still running and it would create a bug if we destroyed it right away.
            //As always, destroying leaks in this case, the timer, is a must. (unless you have a library that recycles them instead)
            call DestroyGroup(getGroup)
            //The group is no longer used at this point, which means that we have a leak. Destroying it would (minimize) remove the leak.
        endif
        call DestroyGroup(ggwp)
        //This group is a dummy group, only used for enumeration. However, since that it would end up always being created, we
        //must destroy it so as not to cause leaks.
        set ggwp = null
        set getGroup = null
        set caster = null
        set t = null
    endfunction

    function Trig_Move_Unit_Actions takes nothing returns boolean
        local unit caster = GetTriggerUnit()
        local real c_x = GetSpellTargetX()
        local real c_y = GetSpellTargetY()
        local timer t
        if GetSpellAbilityId() == 'A000' then
            //This checks whether or not our ability being cast is correct.
            set t = CreateTimer()
            //This creates a timer handle, which we can use to execute a loop
            call SaveUnitHandle(SPELL_HASH, GetHandleId(t), 0, caster)
            call SaveReal(SPELL_HASH, GetHandleId(t), 1, 0.)
            call SaveReal(SPELL_HASH, GetHandleId(t), 2, Atan2(c_y - GetUnitY(caster), c_x - GetUnitX(caster)))
            call SaveGroupHandle(SPELL_HASH, GetHandleId(t), 3, CreateGroup())
            //Messy syntax, but this saves our data in the hashtable SPELL_HASH.
            call TimerStart(t, 0.03125, true, function Timer_Move_Unit_Actions)
            //This starts the timer with a loop.
            //    Q: Why timers when you could just use TriggerSleepAction?
            //    A: TriggerSleepAction is infamous for its inaccuracy in displaying the exact amount of time being displaced.
            //    Now for longer wait times, this would become negligible, but for shorter frequencies, it become annoying
            //    specially if you want to do some sort of gliding.
            set t = null
        endif
        set caster = null
        return false
    endfunction

    //===========================================================================
    function InitTrig_Move_Unit takes nothing returns nothing
        set gg_trg_Move_Unit = CreateTrigger(  )
        call TriggerRegisterAnyUnitEventBJ(gg_trg_Move_Unit, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition( gg_trg_Move_Unit, function Trig_Move_Unit_Actions )
        call UnitAddAbility(gg_unit_hwt3_0057, 'A000')
    endfunction

     
     
    Last edited: Jan 29, 2017
  9. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,107
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    Aniki thanks for the tips, but I tried to make an own (more naive xD) approach. It should work correctly now, though.^^
     
  10. Aniki

    Aniki

    Joined:
    Nov 7, 2014
    Messages:
    517
    Resources:
    4
    Spells:
    1
    JASS:
    3
    Resources:
    4
    I think it does.

    There's this strange check in
    LineSegment.EnumUnits
    though:
    Code (vJASS):

    if GetOwningPlayer(u) != GetLocalPlayer() then // <-- o.O?
        set x = GetUnitX(u)
        set y = GetUnitY(u)

        set x_new = (x-bx)*thetaCos - (y-by)*thetaSin + bx
        set y_new = (x-bx)*thetaSin + (y-by)*thetaCos + by

        if x_new >= minX and x_new <= maxX and y_new >= minY and y_new <= maxY then
            call GroupAddUnit(whichgroup, u)
        endif
    endif // <-- O.o?
     
     
  11. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,107
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    ;D

    Yes, that was from test code, to not enum my own units, thanks mate^^. Will update in an hour or so:).
    I figured it is a bit sad that I don't provide function to rotate a rect/or point, or just check if a point is inside one rotatet rect. :(
    But if wanting such functions one should probably just use polygon, because they will really create/destroy data, and with this one you can just instantly enumerate without any storage. So it's maybe ok, just for this function.
     
  12. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,107
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    Update. EnumItems works now, too.
     
  13. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,255
    Resources:
    6
    Models:
    1
    Icons:
    1
    Spells:
    3
    JASS:
    1
    Resources:
    6
    With enough replies, this could turn into something very useful.
     
  14. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,107
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    Thanks, MyPad. : )

    Btw, if you wanna really share a demo spell, you could also attach the demo map to your post above.
     
  15. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,255
    Resources:
    6
    Models:
    1
    Icons:
    1
    Spells:
    3
    JASS:
    1
    Resources:
    6
    Will do, will do..
    Kudos to you for making this :hand salute:
     
  16. Flux

    Flux

    Joined:
    Feb 6, 2014
    Messages:
    2,334
    Resources:
    28
    Maps:
    1
    Spells:
    19
    Tutorials:
    2
    JASS:
    6
    Resources:
    28
    Ammorth and Anitarf?
     
  17. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,107
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    I stole most logics from them at first attempt + made it gui friendly and a system with all widgets. But then Aniki mentioned a bug, and logics were changed.
     
  18. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,255
    Resources:
    6
    Models:
    1
    Icons:
    1
    Spells:
    3
    JASS:
    1
    Resources:
    6
    Code (vJASS):

    set angle = angle - theta
    set angle = angle*-1
     


    Can be changed to

    Code (vJASS):

    set angle = theta - angle
     
     
  19. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,107
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    Indeed. ;P