• 🏆 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!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

show me the ranges

Status
Not open for further replies.
Level 13
Joined
Nov 7, 2014
Messages
571
A small struct for debugging/seeing ranges/circles (attack ranges, GroupEnumUnitsInRange, TriggerRegisterUnitInRange, etc.).

selection-circles-png.280891


JASS:
// SelectionCircle API
//

// creates a selection circle at point (x, y) with the specified radius;
// format of color: RRGGBBAA (e.g: 0xFF0000FF = red)
SelectionCircle.create_xy takes real x, real y, real radius, integer color returns SelectionCircle

// creates a selection circle with the specified radius and continuously updates its position
// to match that of the unit
SelectionCircle.attach_to_unit takes unit u, real radius, integer color returns SelectionCircle

self.destroy takes nothing returns nothing

// changes the radius of the selection circle
self.set_radius takes real radius returns nothing

// changes the color of the selection circle
self.set_color takes integer color returns nothing

// changes the position of the selection circle;
// should only be used for selection circles created with 'create_xy'
self.set_pos takes real x, real y returns nothing

// changes the line style of the selection circle
self.set_dotted takes boolean flag returns nothing

JASS:
library SelectionCircle initializer init

globals
    // SelectionCircle(s) attached to units are updated this frequently
    private constant real Update_Delay = 1.0 / 32.0
endglobals

globals
    private integer R
    private integer G
    private integer B
    private integer A
endglobals
private function get_rgba takes integer i returns nothing
    local boolean is_neg = false

    if i < 0 then
        set is_neg = true
        set i = i + 0x80000000
    endif

    set A = i - i / 0x100 * 0x100
    set i = i / 0x100
    set B = i - i / 0x100 * 0x100
    set i = i / 0x100
    set G = i - i / 0x100 * 0x100
    set i = i / 0x100
    set R = i

    if is_neg then
        set R = R + 0x80
    endif
endfunction

struct SelectionCircle
    boolean once = true
    string file_name
    image img
    real radius
    integer color

    real x
    real y

    unit target
    integer ul_pos = 0

    method set_radius takes real radius returns nothing
        local SelectionCircle sc = this

        set sc.radius = radius

        // we can't use the check
        //     if sc.img != null
        // because the first image's handle-id is 0 (gj blizzard...)
        // and 0 == null :/
        if sc.once then
            set sc.once = false
        else
            call DestroyImage(sc.img)
        endif

        set sc.img = CreateImage(sc.file_name, /*
            image-width, image-height:*/ 2.0*radius, 2.0*radius, 0.0, /*
            image-position: */ sc.x, sc.y, 0.0, /*
            origin: */ radius, radius, 0.0, /*
            image-type: */ 1 /*
        */)

        call SetImageRenderAlways(sc.img, true)
        call get_rgba(sc.color)
        call SetImageColor(sc.img, R, G, B, A)
    endmethod

    static method create takes real x, real y, real radius, integer color returns SelectionCircle
        local SelectionCircle sc = SelectionCircle.allocate()

        set sc.file_name = "ReplaceableTextures\\Selection\\SelectionCircleLarge.blp"
        set sc.x = x
        set sc.y = y
        set sc.color = color
        call sc.set_radius(radius)

        return sc
    endmethod

    static method create_xy takes real x, real y, real radius, integer color returns SelectionCircle
        return create(x, y, radius, color)
    endmethod

    static SelectionCircle array update_list
    static integer update_list_len = 0
    static code update_cb
    static timer ticker = CreateTimer()

    static method attach_to_unit takes unit u, real radius, integer color returns SelectionCircle
        local SelectionCircle sc = create_xy(GetUnitX(u), GetUnitY(u), radius, color)

        set sc.target = u

        set update_list_len = update_list_len + 1
        set update_list[update_list_len] = sc
        set sc.ul_pos = update_list_len

        if update_list_len == 1 then
            call TimerStart(ticker, Update_Delay, true, update_cb)
        endif

        return sc
    endmethod

    method destroy takes nothing returns nothing
        local SelectionCircle sc = this
        local SelectionCircle last

        call DestroyImage(sc.img)

        if sc.ul_pos != 0 then
            set sc.target = null

            set last = update_list[update_list_len]
            set last.ul_pos = sc.ul_pos
            set update_list[last.ul_pos] = last
            set update_list_len = update_list_len - 1

            if update_list_len == 0 then
                call PauseTimer(ticker)
            endif
        endif

        call sc.deallocate()
    endmethod

    static method update takes nothing returns nothing
        local integer i
        local SelectionCircle sc

        set i = 1
        loop
            exitwhen i > update_list_len
            set sc = update_list[i]
            set sc.x = GetUnitX(sc.target)
            set sc.y = GetUnitY(sc.target)
            call SetImagePosition(sc.img, sc.x, sc.y, 0.0)
            set i = i + 1
        endloop
    endmethod

    method set_color takes integer color returns nothing
        local SelectionCircle sc = this
        set sc.color = color
        call get_rgba(color)
        call SetImageColor(sc.img, R, G, B, A)
    endmethod

    method set_pos takes real x, real y returns nothing
        local SelectionCircle sc = this
        set sc.x = x
        set sc.y = y
        call SetImagePosition(sc.img, x, y, 0.0)
    endmethod

    method set_dotted takes boolean flag returns nothing
        local SelectionCircle sc = this
        if flag then
            set sc.file_name = "ReplaceableTextures\\Selection\\SelectionCircleLargeDotted.blp"
        else
            set sc.file_name = "ReplaceableTextures\\Selection\\SelectionCircleLarge.blp"
        endif
        call sc.set_radius(sc.radius)
    endmethod
endstruct

private function init takes nothing returns nothing
    set SelectionCircle.update_cb = function SelectionCircle.update

    call ExecuteFunc(SCOPE_PRIVATE + "playground")
endfunction

private function playground takes nothing returns nothing
    local player red = Player(0)
    local player blue = Player(1)
    local unit u
    local SelectionCircle sc

    call SetTimeOfDay(12.0)

    set u = CreateUnit(blue, 'hgtw', 0.0, 0.0, 270.0)
    set sc = SelectionCircle.create_xy(GetUnitX(u), GetUnitY(u), 800.0, 0xFF0000FF)
    call sc.set_dotted(true)

    set u = CreateUnit(red, 'Hamg', 0.0, -800.0, 270.0)
    call SelectionCircle.attach_to_unit(u, 700.0, 0xF3DB29FF)
    set u = CreateUnit(red, 'hmtm', -1200.0, -1200.0, 270.0)
    call SelectionCircle.attach_to_unit(u, 1250.0, 0xFF00FFFF)
endfunction

endlibrary
 

Attachments

  • selection-circles.png
    selection-circles.png
    685.4 KB · Views: 815
Last edited:
Level 26
Joined
Aug 18, 2009
Messages
4,097
At this point I would like to ask: Anyone cared to implement Data Binding in Wc3 yet? Like make the attack range of a unit a property object that exposes a real value for example and make the radius of your circle a property of the same type, then establish a uni- or bilateral connection between both, so the circle radius automatically updates with attack range.
 
Level 7
Joined
Jan 23, 2011
Messages
350
Check the ranges 32 times per second for all the units with the circle on?? You could also make "set_radius(UnitRange)" on every period, but that would be a waste of resource. Honestly, data binding just seem like a fancy name for periodic checks xd
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
It's via listeners but abstracts them away. Polling (a fancy name for periodic checks) is inaccurate, might be underperforming and is not desired depending on the use case but it could be included, too. The point of data binding is to simplify and streamline the observation of variables, turning them into properties. Normally you would like write a registration method per variable to listen to its value changes and a setter method for a variable which invokes the registered events along with changing the state, on the application side you would usually call the registration method, write the listening method with update and additionally execute the update at start + clean up. It's relatively easy to miss something. Property wrappers/data binding minimizes that task and there is some more issues taken care of like only keeping weak references to avoid memory leaks.

so it would be like:

Code:
//server
def var;

def addVarListener(handler) {
  //add handler to some collection
}

def setVar(val) {
  var = val;
  //invoke handlers
}

//client
def var2;

def someFunc() {
  addVarListener(update)

  update();
}

def update() {
  var2 = var;
}

vs

Code:
//server
def varProperty = new Property();

//client
def var2Property = new Property();

def someFunc() {
  var2Property.bind(varProperty);
}

If you want to do more than just binding one variable to another, you can write

Code:
def someFunc() {
  var2Property.addListener(update)
}

def update() {
  //whatever
}

or do both. And there is usually some more functionality therein like you do not only get the new value passed to the handler but the previous value, too, to review the difference.
 
Last edited:
Level 7
Joined
Jan 23, 2011
Messages
350
Closer you get to a a VarListener would be the Real variable event, and i don't think those are really dynamic(you would have to destroy and recreate the trigg with another event Every time it runs). I would still use a Timer to create the same result, as we are talking about War3 and not a serious programming language
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
TRVA is very restricted and actually I think one should not use it at all since it takes a string. It introduces the same problem as ExecuteFunc of breaking code analysis when there is dynamic input. There are event and timer systems and people are actually taking it pretty seriously, see the lot that is currently attacking the jass engine or modding the game altogether. A high frequent timer should at least check for the variable change before making expensive native calls as illustrated above. Do not misunderstand: Polling can indeed be more efficient but if the value is seldom changed, it does not make sense. I usually employed timers for animation tasks where accuracy is not an issue and more direct methods for gameplay relevant stuff if feasible. You can also combine both approaches like to prevent a too frequent update.
 
Level 13
Joined
Oct 18, 2013
Messages
693
How does one figure out where and which non-local selection circle/s are, like cleaning/destroying them after a few seconds? Seems there is no expire or catch code for it, wondering if this'll get an update?
Not much of an issue, just handle the timers yourself. What I'm stuck on is getting the circles drawn when you click an ability, dk how to catch that event.
 
Level 13
Joined
Oct 18, 2013
Messages
693
This is a neat library utilizing war3s circle drawing api, but there are some issues with it atm.

Looking through the code, nothing sticks out to me as problem-causing...Perhaps there is some issue with the SelectionCircle array I use to instance each circle? I really don't know.
 
Level 13
Joined
Oct 18, 2013
Messages
693
Neat-o library. This is very useful for adding some interface to an otherwise already-interesting game. I'm using this in the Team Contest - Creating a Hive member!
I also added some sort of API at my behest.
Feel free to provide any API you find useful, friend. also, have you experienced the same issues as me using this? (selection circles drawn on other unit, random circles being drawn on units, etc.)

edit: I think these issues might be coming from cutscene GUI actions, I've noticed troubles with cutscenes interacting with Memory in strange ways when I changed the Gold Resource Text color, would be set to black and couldnt be set back after the cutscene. Cinematics can apparantly also cause Ghost-Selection circles (black circle around a unit when you aren't selecting it)

Anyone willing to help run some tests with me to figure out what Citematic action could be causing this?
 
Last edited:
JASS:
library SelectionCircle initializer init
  
    /*  SelectionCircle
    *       -   By Aniki
    *       -   Documentation added out of good will.
    *      
    *   Link:   https://www.hiveworkshop.com/threads/show-me-the-ranges.299681/#post-3201943
    *
    *   ________
    *
    *     API
    *   ________
    *
    *       function get_rgba(integer i)
    *           Sets the globals R, G, B, A to their respective values. Integer i is a hex parameter.
    *
    *   struct SelectionCircle
    *           boolean once = true
    *           string file_name
    *           image img
    *           real radius
    *           integer color
    *      
    *           real x
    *           real y
    *
    *           unit target
    *           integer ul_pos = 0
    *
    *           static SelectionCircle array update_list
    *           static integer update_list_len
    *           static code update_cb
    *           static timer ticker = CreateTimer()
    *
    *       method set_radius(real radius)
    *           - Sets the radius of the circle.
    *
    *       static method create(real x, real y, real radius, integer color) returns SelectionCircle
    *           - Constructs a SelectionCircle instance.
    *
    *       static method create_xy(real x, real y, real radius, integer color) returns SelectionCircle
    *           - A wrapper for the constructor above.
    *
    *       static method attach_to_unit(unit u, real radius, integer color) returns SelectionCircle
    *           - Internally calls create_xy and binds it to a list, of which will be iterated over.
    *
    *       method destroy()
    *           - Destroys a SelectionCircle instance.
    *
    *       method set_pos(real x, real y)
    *           - Sets the position of a circular object of a SelectionCircle instance.
    *
    *       method set_dotted(boolean flag)
    *           - Modifies the external properties of the circular object.
    *
    *       Added: (Did not originally exist)
    *           private trigger exec_trig
    *           private triggercondition exec
    *           private integer data
    *           private boolean visible
    *
    *           static SelectionCircle current
    *
    *       method set_listener_event(code exe)
    *           - Removes a previous set of instructions and adds a new set of instructions
    *             to the listening trigger instance.
    *
    *       method set_show(boolean flag)
    *           - Shows or hides a SelectionCircle (untested).
    *
    *       End API description.
    */

    globals
        // SelectionCircle(s) attached to units are updated this frequently
        private constant real Update_Delay = 1.0 / 32.0
    endglobals

    globals
        private integer R
        private integer G
        private integer B
        private integer A
    endglobals
  
    private function get_rgba takes integer i returns nothing
        local boolean is_neg = false

        if i < 0 then
            set is_neg = true
            set i = i + 0x80000000
        endif

        set A = i - i / 0x100 * 0x100
        set i = i / 0x100
        set B = i - i / 0x100 * 0x100
        set i = i / 0x100
        set G = i - i / 0x100 * 0x100
        set i = i / 0x100
        set R = i

        if is_neg then
            set R = R + 0x80
        endif
    endfunction

    struct SelectionCircle
        //  Added for event listening.
        trigger exec_trig
        triggercondition exec
        boolean visible
        integer data
      
        boolean once = true
        string file_name
        image img
        real radius
        integer color

        real x
        real y

        unit target
        integer ul_pos = 0

        method set_radius takes real radius returns nothing
            local SelectionCircle sc = this

            set sc.radius = radius

            // we can't use the check
            //     if sc.img != null
            // because the first image's handle-id is 0 (gj blizzard...)
            // and 0 == null :/
            if sc.once then
                set sc.once = false
            else
                call DestroyImage(sc.img)
            endif

            set sc.img = CreateImage(sc.file_name, /*
                image-width, image-height:*/ 2.0*radius, 2.0*radius, 0.0, /*
                image-position: */ sc.x, sc.y, 0.0, /*
                origin: */ radius, radius, 0.0, /*
                image-type: */ 1 /*
            */)

            call SetImageRenderAlways(sc.img, true)
            call get_rgba(sc.color)
            call SetImageColor(sc.img, R, G, B, A)
        endmethod
      
        static method create takes real x, real y, real radius, integer color returns SelectionCircle
            local SelectionCircle sc = SelectionCircle.allocate()

            set sc.file_name = "ReplaceableTextures\\Selection\\SelectionCircleLarge.blp"
            set sc.x = x
            set sc.y = y
            set sc.color = color
            set sc.data = 0
          
            call sc.set_radius(radius)

            return sc
        endmethod

        static method create_xy takes real x, real y, real radius, integer color returns SelectionCircle
            return create(x, y, radius, color)
        endmethod

        static SelectionCircle array update_list
        static SelectionCircle current = 0
      
        static integer update_list_len = 0
        static code update_cb
        static timer ticker = CreateTimer()
      
        //  Modified to accomodate the event listener
        static method attach_to_unit takes unit u, real radius, integer color returns SelectionCircle
            local SelectionCircle sc = create_xy(GetUnitX(u), GetUnitY(u), radius, color)

            set sc.target = u
            set sc.exec_trig = CreateTrigger()
          
            set update_list_len = update_list_len + 1
            set update_list[update_list_len] = sc
            set sc.ul_pos = update_list_len

            if update_list_len == 1 then
                call TimerStart(ticker, Update_Delay, true, update_cb)
            endif

            return sc
        endmethod

        method destroy takes nothing returns nothing
            local SelectionCircle sc = this
            local SelectionCircle last

            call DestroyImage(sc.img)

            if sc.ul_pos != 0 then
                call DestroyTrigger(exec_trig)
              
                set sc.target = null
              
                set last = update_list[update_list_len]
                set last.ul_pos = sc.ul_pos
                set update_list[last.ul_pos] = last
                set update_list_len = update_list_len - 1

                if update_list_len == 0 then
                    call PauseTimer(ticker)
                endif
            endif
          
            set exec = null
          
            call sc.deallocate()
        endmethod

        static method update takes nothing returns nothing
            local integer i
            local SelectionCircle sc

            set i = 1
            loop
                exitwhen i > update_list_len
              
                set SelectionCircle.current = update_list[i]
                set sc = SelectionCircle.current
                set sc.x = GetUnitX(sc.target)
                set sc.y = GetUnitY(sc.target)
                call SetImagePosition(sc.img, sc.x, sc.y, 0.0)
              
                call TriggerEvaluate(sc.exec_trig)
                set i = i + 1
            endloop
        endmethod

        method set_color takes integer color returns nothing
            local SelectionCircle sc = this
            set sc.color = color
            call get_rgba(color)
            call SetImageColor(sc.img, R, G, B, A)
        endmethod

        method set_pos takes real x, real y returns nothing
            local SelectionCircle sc = this
            set sc.x = x
            set sc.y = y
            call SetImagePosition(sc.img, x, y, 0.0)
        endmethod

        method set_dotted takes boolean flag returns nothing
            local SelectionCircle sc = this
            if flag then
                set sc.file_name = "ReplaceableTextures\\Selection\\SelectionCircleLargeDotted.blp"
            else
                set sc.file_name = "ReplaceableTextures\\Selection\\SelectionCircleLarge.blp"
            endif
            call sc.set_radius(sc.radius)
        endmethod
      
        method set_listener_event takes code exe returns nothing
            local SelectionCircle sc = this
            if sc.ul_pos != 0 then
                call TriggerRemoveCondition(sc.exec_trig, sc.exec)
                set exec = TriggerAddCondition(sc.exec_trig, Condition(exe))
            endif
        endmethod
      
        method set_show takes boolean flag returns nothing
            local SelectionCircle sc = this
            call SetImageRenderAlways(sc.img, flag)
        endmethod
    endstruct
  
    private function init takes nothing returns nothing
        set SelectionCircle.update_cb = function SelectionCircle.update
    endfunction

endlibrary

Feel free to provide any API you find useful, friend. also, have you experienced the same issues as me using this? (selection circles drawn on other unit, random circles being drawn on units, etc.)

I have not yet tested it with the Cinematics, but since I'm just using it as it is, I'm quite fine with the library itself. Of course, I may just hypothesize that the probable cause is the transmission function, which might affect how selection circles behave. There may be parts in
the natives called that internally tinkers with the image section of the dll. Just my guess, though, I do not roll with dll-s yet.
 
Last edited:
Status
Not open for further replies.
Top