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

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

Status
Not open for further replies.
Level 6
Joined
Aug 28, 2015
Messages
213
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.
JASS:
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())
 
Level 23
Joined
Jan 1, 2009
Messages
1,610
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.

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.
 
Level 6
Joined
Aug 28, 2015
Messages
213
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?
 
Level 23
Joined
Jan 1, 2009
Messages
1,610
I couldn't use tuples because I thought to use a vec2 array

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.
 
Level 6
Joined
Aug 28, 2015
Messages
213
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.
 
Level 6
Joined
Aug 28, 2015
Messages
213
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:
JASS:
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:
Level 23
Joined
Jan 1, 2009
Messages
1,610
Sorry I think I used the wrong terminology that I think leaded to misunderstanding.

Yeah, you need to be more specific. Nothing in your initial posts talks about this consecutive line business.

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.

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.
 
Level 6
Joined
Aug 28, 2015
Messages
213
Yeah, you need to be more specific. Nothing in your initial posts talks about this consecutive line business.
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:


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.
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.
JASS:
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.
 
Level 23
Joined
Jan 1, 2009
Messages
1,610
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?
 
Status
Not open for further replies.
Top