1. 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
  2. The Melee Mapping Contest #4: 2v2 - Results are out! Step by to congratulate the winners!
    Dismiss Notice
  3. We're hosting the 15th Mini-Mapping Contest with YouTuber Abelhawk! The contestants are to create a custom map that uses the hidden content within Warcraft 3 or is inspired by any of the many secrets within the game.
    Dismiss Notice
  4. The 20th iteration of the Terraining Contest is upon us! Join and create exquisite Water Structures for it.
    Dismiss Notice
  5. Check out the Staff job openings thread.
    Dismiss Notice

[Solved] [Wurst] How to add a drag-line for each player to Mouse events?

Discussion in 'Triggers & Scripts' started by apsyll, Mar 12, 2019.

  1. apsyll

    apsyll

    Joined:
    Aug 28, 2015
    Messages:
    184
    Resources:
    1
    Maps:
    1
    Resources:
    1
    Hey working on this system for a while and got to a point I'm starting to like it,
    now I would also to like to add the function to create a line(vec2 array + Player Id) that follows your mouse cursor when a button is pressed and will be returned on the release and can be destroyed after it is used in another function.

    This is what I had in mind: vec2 array dragLine, mouseButtonType wichButtonPressed, integer playerId

    I thought about something that periodically checks the distance between the last coordination of the last added point in the array and the current mouse position.
    If it is over a buffer range a new point with the position of the mouse cursor will be added and used as the reference point.
    This is my package atm. I will split it in two different ones to make it easier to use but wanted to have something before I start polishing.
    Code (vJASS):

    package MouseUtils
    import ClosureEvents
    import  ClosureTimers

    /*
        add drag line vec2 2darray
        seperate in two different packages
    */

    enum MouseCurrentEvent
        MOUSE_PRESED_LEFT
        MOUSE_RELEASED_LEFT
        MOUSE_PRESED_RIGHT
        MOUSE_RELEASED_RIGHT
        MOUSE_PRESED_MIDDLE
        MOUSE_RELEASED_MIDDLE
        MOUSE_MOVED
        MOUSE_STOPPED

    public timer array mouseTimerLeft
    public timer array mouseTimerRight
    public timer array mouseTimerMiddle
    public timer array mouseTimerMove
    public integer array mouseClickCounterLeft
    public integer array mouseClickCounterRight
    public integer array mouseClickCounterMiddle

    public tuple mouseTuple(MouseCurrentEvent currentEvent, boolean left, boolean right, boolean middle, boolean move, vec2 mousePosition)

    mouseTuple array mouseStatuses

    public function player.getMouseButtons() returns mouseTuple
        return mouseStatuses[this.getId()]

    EventListener firstKeyListener = null

    public function addMouseListener(EventListener listener) returns EventListener
        if firstKeyListener != null
            firstKeyListener.prev = listener
            listener.next = firstKeyListener

        firstKeyListener = listener
        return listener

    public function removeMouseListener(EventListener listener)
        if firstKeyListener == listener
            firstKeyListener = null
        destroy listener

    public function setClickCounter(mousebuttontype mb, integer playerId, integer count)
        if mb == MOUSE_BUTTON_TYPE_LEFT
            mouseClickCounterLeft[playerId] = count
        else if mb == MOUSE_BUTTON_TYPE_RIGHT
            mouseClickCounterRight[playerId] = count
        else if mb == MOUSE_BUTTON_TYPE_MIDDLE
            mouseClickCounterMiddle[playerId] = count

    public function getClickCounter(mousebuttontype mb, integer playerId) returns int
        if mb == MOUSE_BUTTON_TYPE_LEFT
            return mouseClickCounterLeft[playerId]
        else if mb == MOUSE_BUTTON_TYPE_RIGHT
            return mouseClickCounterRight[playerId]
        else if mb == MOUSE_BUTTON_TYPE_MIDDLE
            return mouseClickCounterMiddle[playerId]
        return 0

    public function resetClickCounter(mousebuttontype mb, integer playerId)
        if mb == MOUSE_BUTTON_TYPE_LEFT
            mouseClickCounterLeft[playerId] = 0
        else if mb == MOUSE_BUTTON_TYPE_RIGHT
            mouseClickCounterRight[playerId] = 0
        else if mb == MOUSE_BUTTON_TYPE_MIDDLE
            mouseClickCounterMiddle[playerId] = 0

    function onMouseStopMove(integer playerId)
        mouseStatuses[playerId].move = false
        mouseStatuses[playerId].currentEvent = MOUSE_STOPPED
               
    function onMouseEvent()
        let playerId = GetTriggerPlayer().getId()
        var mouseStatus = mouseStatuses[playerId]
        let resetTime   = 1.00 / 30
        let id = GetTriggerEventId()
        let mb = BlzGetTriggerPlayerMouseButton()
        if id == EVENT_PLAYER_MOUSE_DOWN
            if mb == MOUSE_BUTTON_TYPE_LEFT
                mouseStatus.left = true
                mouseStatus.currentEvent = MOUSE_PRESED_LEFT
            else if mb == MOUSE_BUTTON_TYPE_RIGHT
                mouseStatus.right = true
                mouseStatus.currentEvent = MOUSE_PRESED_RIGHT
            else if mb == MOUSE_BUTTON_TYPE_MIDDLE
                mouseStatus.middle = true
                mouseStatus.currentEvent = MOUSE_PRESED_MIDDLE
            setClickCounter(mb, playerId, getClickCounter(mb, playerId) + 1)
        else if id == EVENT_PLAYER_MOUSE_UP
            // reset timer that sets the multible click counter to 0 after resetTime
            timer resetTimer = null
            if mb == MOUSE_BUTTON_TYPE_LEFT
                mouseStatus.left = false
                mouseStatus.currentEvent = MOUSE_RELEASED_LEFT
                resetTimer = mouseTimerLeft[playerId]
            else if mb == MOUSE_BUTTON_TYPE_RIGHT
                mouseStatus.right = false
                mouseStatus.currentEvent = MOUSE_RELEASED_RIGHT
                resetTimer = mouseTimerRight[playerId]
            else if mb == MOUSE_BUTTON_TYPE_MIDDLE
                mouseStatus.middle = false
                mouseStatus.currentEvent = MOUSE_RELEASED_MIDDLE
                resetTimer = mouseTimerMiddle[playerId]
            resetTimer.doAfter(resetTime) ->
                resetClickCounter(mb, playerId)
        else if id == EVENT_PLAYER_MOUSE_MOVE
            mouseStatus.move = true
            mouseStatus.currentEvent = MOUSE_MOVED
            mouseTimerMove[playerId].doAfter(resetTime) ->
                onMouseStopMove(playerId)
        mouseStatus.mousePosition = vec2(BlzGetTriggerPlayerMouseX(),BlzGetTriggerPlayerMouseY())
        mouseStatuses[playerId] = mouseStatus

        var listener = firstKeyListener
        while listener != null
            listener.onEvent()
            listener = listener.next

    init
        for i = 0 to 23
            mouseStatuses[i] = mouseTuple(MouseCurrentEvent.MOUSE_STOPPED, false, false, false, false, vec2(0., 0.))
        EventListener.add(EVENT_PLAYER_MOUSE_DOWN, () -> onMouseEvent())
        EventListener.add(EVENT_PLAYER_MOUSE_UP, () -> onMouseEvent())
        EventListener.add(EVENT_PLAYER_MOUSE_MOVE, () -> onMouseEvent())
     
     
  2. Frotty

    Frotty

    Wurst Reviewer

    Joined:
    Jan 1, 2009
    Messages:
    1,392
    Resources:
    10
    Models:
    3
    Tools:
    1
    Maps:
    4
    Tutorials:
    1
    Wurst:
    1
    Resources:
    10
    Why periodic check? There is a mouse move event. And you already have that event "MOUSE_MOVED". Just save the position on PRESED (btw it's preSSed) event, then listen to MOVED events and stop it after RELEASED event and see what/if line was drawn. If yes, fire all listeners to your new "MOUSE_DRAG_LINE" event.
     
  3. apsyll

    apsyll

    Joined:
    Aug 28, 2015
    Messages:
    184
    Resources:
    1
    Maps:
    1
    Resources:
    1
    Thank you for the reply.
    Yes forgot that I could use the move event, it's just too new, even I have it right in front of me...
    The core question would be how do I store the data should I use a class? I couldn't use tuples because I thought to use a vec2 array or should I use linked list to store the locations?
     
  4. Frotty

    Frotty

    Wurst Reviewer

    Joined:
    Jan 1, 2009
    Messages:
    1,392
    Resources:
    10
    Models:
    3
    Tools:
    1
    Maps:
    4
    Tutorials:
    1
    Wurst:
    1
    Resources:
    10
    I don't get it, you can use tuples, and you can use vec2, even nested tuples work.
    tuple dragData(vec2 startpos, vec2 endpos)
    then
    dragData array dragDatas


    You already copied a tuple that works like this,
    tuple mouseTuple
    is also set in an array per player.
    After you have the functionality you can add better API via ext. funcs.
     
  5. apsyll

    apsyll

    Joined:
    Aug 28, 2015
    Messages:
    184
    Resources:
    1
    Maps:
    1
    Resources:
    1
    First of all thank you for your help.
    Sorry I think I used the wrong terminology that I think leaded to misunderstanding.
    dragPath.png <- This is what I try to do,
    a Path made out of vec2 so I would have to store for each player, for each mouse button an array of vec2s.
    I try atm with creating a class for each vector and store it in a hashmap with the player id as parent.
    If there is a easier solution I would be happy to get some nudge in the right direction I am still pretty unfamiliar to Wurst Script.
     
  6. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,562
    Resources:
    17
    Maps:
    1
    Spells:
    10
    Tutorials:
    6
    Resources:
    17
    What you'd want is an LinkedList of vec2 (unsure if that is legal or not actually) which is accessed by a hashmap most likely.

    If it is not legal, make the list out of classes which have a vec2
     
  7. apsyll

    apsyll

    Joined:
    Aug 28, 2015
    Messages:
    184
    Resources:
    1
    Maps:
    1
    Resources:
    1
    I do it atm with the classes looks a bit messy because it is in the middle of creating but this is what I got:
    Code (vJASS):

    class DragVec
        let vec = vec3(0, 0, 0)
        construct(vec2 v, bool withTerrainZ)
            vec = v.toVec3()
            if withTerrainZ
                vec = v.withTerrainZ()
       
        function getVector() returns vec3
            return vec

    class DragPath
        constant bufferrange = 10.00
        vec2 currentMousePosition
        let dragPath = new HashList<DragVec>
        mousebuttontype mb
        int playerId

        construct(real x, real y, mousebuttontype mb, int playerId)
            this.playerId = playerId
            this.mb = mb
            currentMousePosition = vec2(x, y)
            dragPath.add(new DragVec(currentMousePosition, true))
       
        function getPlayerId() returns int
            return this.playerId
       
        //returns true if a new point is set
        function onMouseMove(real x, real y) returns bool
            this.currentMousePosition = vec2(x, y)
            let oldMousePosition = dragPath.get(0).getVector().toVec2()
            if currentMousePosition.distanceToSq(oldMousePosition) >= this.bufferrange
                dragPath.add(new DragVec(currentMousePosition, true))
                return true
            return false
     
    Last edited: Mar 14, 2019
  8. Frotty

    Frotty

    Wurst Reviewer

    Joined:
    Jan 1, 2009
    Messages:
    1,392
    Resources:
    10
    Models:
    3
    Tools:
    1
    Maps:
    4
    Tutorials:
    1
    Wurst:
    1
    Resources:
    10
    Yeah, you need to be more specific. Nothing in your initial posts talks about this consecutive line business.

    What's wrong with your current solution? I would say it's messy because your code style is bad. Implementation seems fine.
    Just a simple event stack.
     
  9. apsyll

    apsyll

    Joined:
    Aug 28, 2015
    Messages:
    184
    Resources:
    1
    Maps:
    1
    Resources:
    1
    Yes that's why I added more information later, sorry for this confusion.
    For me it made sense because of course I know what I was talking about:rolleyes:


    there is nothing wrong I was just about to change up some stuff got now something that looks okay for me.
    Sad but true I guess, that happens when you stick so long to GUI...
    I will refactor this soon again but still want to show an updated version of my code for further feedback.
    Code (vJASS):

    package MouseUtils
    import ClosureEvents
    import  ClosureTimers
    import TimerUtils
    import HashList

    /*
        seperate in two different packages
    */

    enum MouseCurrentEvent
        MOUSE_PRESSED_LEFT
        MOUSE_RELEASED_LEFT
        MOUSE_PRESSED_RIGHT
        MOUSE_RELEASED_RIGHT
        MOUSE_PRESSED_MIDDLE
        MOUSE_RELEASED_MIDDLE
        MOUSE_MOVED
        MOUSE_STOPPED

    public tuple mouseTuple(MouseCurrentEvent currentEvent, boolean left, boolean right, boolean middle, boolean move, vec2 mousePosition, timer moveTimer)
    public tuple clickTuple(int left, int right, int middle, DragPath leftPath, DragPath rightPath, DragPath middlePath, timer leftTimer, timer rightTimer, timer middleTimer)
    mouseTuple array mouseStatuses
    clickTuple array clickStatuses

    public function player.getMouseButtons() returns mouseTuple
        return mouseStatuses[this.getId()]

    public function player.getMouseClicks() returns clickTuple
        return clickStatuses[this.getId()]

    EventListener firstKeyListener = null

    public function addMouseListener(EventListener listener) returns EventListener
        if firstKeyListener != null
            firstKeyListener.prev = listener
            listener.next = firstKeyListener

        firstKeyListener = listener
        return listener

    public function removeMouseListener(EventListener listener)
        if firstKeyListener == listener
            firstKeyListener = null
        destroy listener

    public function player.setClickCounter(mousebuttontype mb, integer count)
        if mb == MOUSE_BUTTON_TYPE_LEFT
            clickStatuses[this.getId()].left = count
        else if mb == MOUSE_BUTTON_TYPE_RIGHT
            clickStatuses[this.getId()].right = count
        else if mb == MOUSE_BUTTON_TYPE_MIDDLE
            clickStatuses[this.getId()].middle = count

    public function player.getClickCounter(mousebuttontype mb) returns int
        if mb == MOUSE_BUTTON_TYPE_LEFT
            return clickStatuses[this.getId()].left
        else if mb == MOUSE_BUTTON_TYPE_RIGHT
            return clickStatuses[this.getId()].right
        else if mb == MOUSE_BUTTON_TYPE_MIDDLE
            return clickStatuses[this.getId()].middle
        return 0

    function onMouseStopMove(integer playerId)
        mouseStatuses[playerId].move = false
        mouseStatuses[playerId].currentEvent = MOUSE_STOPPED

    class DragVec
        let vec = vec3(0, 0, 0)
        construct(vec2 v, bool withTerrainZ)
            vec = v.toVec3()
            if withTerrainZ
                vec = v.withTerrainZ()
     
        function getVector() returns vec3
            return vec

    class DragPath
        constant bufferrange = 10.00
        vec2 currentMousePosition
        vec2 oldMousePosition
        let path = new HashList<DragVec>
        int playerId

        construct(vec2 v)
            this.playerId = playerId
            this.currentMousePosition = v
            path.add(new DragVec(currentMousePosition, true))
            this.oldMousePosition = this.currentMousePosition
     
        //returns true if a new point is set
        function onMouseMove(vec2 v) returns bool
            currentMousePosition = v
            if currentMousePosition.distanceToSq(oldMousePosition) >= bufferrange
                path.add(new DragVec(currentMousePosition, true))
                oldMousePosition = currentMousePosition
                return true
            return false

        function getPath() returns vec3 array
            vec3 array vec
            for i = 0 to this.path.size()
                vec[i] = path.get(i).getVector()
            return vec

        function onRelease(vec2 v)
            currentMousePosition = v
            path.add(new DragVec(currentMousePosition, true))
            oldMousePosition = currentMousePosition
     
        ondestroy
            path.clear()

    function onMouseEvent()
        let playerId = GetTriggerPlayer().getId()
        var mouseStatus = mouseStatuses[playerId]
        var clickStatus = clickStatuses[playerId]
        let resetTime   = 1.00 / 20.00
        let id = GetTriggerEventId()
        let mb = BlzGetTriggerPlayerMouseButton()
        mouseStatus.mousePosition = vec2(BlzGetTriggerPlayerMouseX(),BlzGetTriggerPlayerMouseY())
        if id == EVENT_PLAYER_MOUSE_DOWN
            if mb == MOUSE_BUTTON_TYPE_LEFT
                mouseStatus.left = true
                mouseStatus.currentEvent = MOUSE_PRESSED_LEFT
                //package #2
                clickStatus.left = clickStatus.left + 1
                clickStatus.leftPath = new DragPath(mouseStatus.mousePosition)
            else if mb == MOUSE_BUTTON_TYPE_RIGHT
                mouseStatus.right = true
                mouseStatus.currentEvent = MOUSE_PRESSED_RIGHT
                //#2
                clickStatus.right = clickStatus.right + 1
                clickStatus.rightPath = new DragPath(mouseStatus.mousePosition)
            else if mb == MOUSE_BUTTON_TYPE_MIDDLE
                mouseStatus.middle = true
                mouseStatus.currentEvent = MOUSE_PRESSED_MIDDLE
                //#2
                clickStatus.middle = clickStatus.middle +1
                clickStatus.middlePath = new DragPath(mouseStatus.mousePosition)
        else if id == EVENT_PLAYER_MOUSE_UP
            if mb == MOUSE_BUTTON_TYPE_LEFT
                mouseStatus.left = false
                mouseStatus.currentEvent = MOUSE_RELEASED_LEFT
                //#2 Reset the counter after a short period
                //The timer should be restarted on every release event
                clickStatus.leftTimer.doAfter(resetTime) ->
                    clickStatuses[playerId].left = 0
                clickStatus.leftPath.onRelease(mouseStatus.mousePosition)
                //do something later with the drawn path
                //vec3 array leftPath = clickStatus.leftPath.getPath()
                destroy clickStatus.leftPath
            else if mb == MOUSE_BUTTON_TYPE_RIGHT
                mouseStatus.right = false
                mouseStatus.currentEvent = MOUSE_RELEASED_RIGHT
                //#2
                clickStatus.rightTimer.doAfter(resetTime) ->
                    clickStatuses[playerId].right = 0
                clickStatus.rightPath.onRelease(mouseStatus.mousePosition)
                //do something later with the drawn path
                //vec3 array rightPath = clickStatus.rightPath.getPath()
                destroy clickStatus.rightPath
            else if mb == MOUSE_BUTTON_TYPE_MIDDLE
                mouseStatus.middle = false
                mouseStatus.currentEvent = MOUSE_RELEASED_MIDDLE
                //#2
                clickStatus.middleTimer.doAfter(resetTime) ->
                    clickStatuses[playerId].middle = 0
                clickStatus.middlePath.onRelease(mouseStatus.mousePosition)
                //do something later with the drawn path
                //vec3 array middlePath = clickStatus.middlePath.getPath()
                destroy clickStatus.middlePath
        else if id == EVENT_PLAYER_MOUSE_MOVE
            mouseStatus.move = true
            mouseStatus.currentEvent = MOUSE_MOVED
            mouseStatus.moveTimer.doAfter(resetTime) ->
                onMouseStopMove(playerId)
            //#2
            if mouseStatus.left
                clickStatus.leftPath.onMouseMove(mouseStatus.mousePosition)
            if mouseStatus.right
                clickStatus.rightPath.onMouseMove(mouseStatus.mousePosition)
            if mouseStatus.middle
                clickStatus.middlePath.onMouseMove(mouseStatus.mousePosition)
         
        mouseStatuses[playerId] = mouseStatus
        clickStatuses[playerId] = clickStatus

        var listener = firstKeyListener
        while listener != null
            listener.onEvent()
            listener = listener.next

    init
        for i = 0 to 23
            mouseStatuses[i] = mouseTuple(MouseCurrentEvent.MOUSE_STOPPED, false, false, false, false, vec2(0., 0.), getTimer())
            clickStatuses[i] = clickTuple(0, 0, 0, null, null, null, getTimer(), getTimer(), getTimer())
        EventListener.add(EVENT_PLAYER_MOUSE_DOWN, () -> onMouseEvent())
        EventListener.add(EVENT_PLAYER_MOUSE_UP, () -> onMouseEvent())
        EventListener.add(EVENT_PLAYER_MOUSE_MOVE, () -> onMouseEvent())
     

    Thank you all for your help.
     
  10. Frotty

    Frotty

    Wurst Reviewer

    Joined:
    Jan 1, 2009
    Messages:
    1,392
    Resources:
    10
    Models:
    3
    Tools:
    1
    Maps:
    4
    Tutorials:
    1
    Wurst:
    1
    Resources:
    10
    Okay, some more feedback then. I don't think you need all "DragPath leftPath, DragPath rightPath, DragPath middlePath" because once you hit another mouse button, the last path is over right? Or do you need retroactive access to the last path done with each button? I think you should just save all mouse events in a stack and then reconstruct a path from that as needed.
    Right now onMouseEvent is quite messy with many duplicated lines which should be cleaned up.
    But even worse, why do you have 3 timers and doing nothing with them?! You are just doing a "doAfter", you need exactly 0 timers.
    clickStatus.leftTimer.doAfter(resetTime) ->
    should just be
    doAfter(resetTime) ->
    . Doing weird stuff like this for seemingly no purpose makes me wonder if you read the package hotdoc or online doc page WurstScript • Closure Timers.

    Also stuff like this is bad style:
    • let vec = vec3(0, 0, 0)
      Use descriptive names, use ZERO3 for the zero length vector instead of vec3(0,0,0)
    • This function
      function onMouseStopMove(integer playerId)
      starts with "on" but isn't of callback nature. Also only used once. Inline it.
    • Use ternary if it makes sense, e.g. DragVec constructor
      vec = withTerrainZ ? v.withTerrainZ() : v.toVec3()
      .
    • This
      this.playerId = playerId
      should throw a warning. You are assigning a field to itself here, there is no parameter.
    • int and integer are fine even when I prefer int, but definitely don't mix both in the same file
    • function getPath() returns vec3 array
      why? You should just work on the list. Also why are you using HashList?
     
  11. apsyll

    apsyll

    Joined:
    Aug 28, 2015
    Messages:
    184
    Resources:
    1
    Maps:
    1
    Resources:
    1
    Thank you so much for your feedback
    This Thread went a bit out of topic so I created a new Thread at the Lab.
    For people interested in the final code -> [Wurst] MouseUtils
    Thank you again for the support.
     
    Last edited: Mar 16, 2019