System ForceKeySystem

Executor

I see you
Reaction score
57
ForceKeySystem v1.1
by Executor​

  • Language = vJass
  • Requires = Event;
  • MPI

ForceKey?
This system allows you to activate a ForceKey-mode for each player which means that aslong as the mode is active he will always have selected only a dummy unit. The user is forced to send a hotkey signal to the dummy unit preparing it all the time to cast a dummy spell. This means if you click with left click somewhere on the map it will be recognized as an order you may react to.

Screenshot:
061109003914_ForceKeyScreen.JPG

141109141405_ForceKeyScreen.JPG

Interface and Documentation:
JASS:
// This system provides the possibility to react to left mouse clicks.
// Therefore when turned on, a dummy will permanently be selected and
// a hotkey for a 'selection' spell gets issued. This way you are 
// permanently in 'targeting' mode (cursor => target cross) and leftclicking
// leads to ordering the dummy to cast. I block the cast and process the
// information given by it, ( x , y , player ).

// Additional to that you may force that your next click is 'special', the 
// special click ability may have different target settings than the normal ability.

// For example, normal leftclicking (on ground) leads to blizzards being casted
// but hitting escape before clicking allows you to target units and will affect
// with a stormbolt

// IMPORTANT: I use the unituserdata of my dummy units.



// ForceKey Interface ( slower "pure" JASS version)

    function TriggerRegisterForceKeyClickEvent takes trigger t returns EventReg
        // which trigger shall fire on click?

    function EnableForceKeyForPlayer takes player p returns nothing
        // turn on forcekey mode for specific player
    
    function DisableForceKeyForPlayer takes player p returns nothing
        // turn off forcekey mode for specific player
    
    function ForceSpecialClick takes player p returns nothing
        // next click will use the 'special' ability

    // 'Normal' event responses
        function GetClickX takes nothing returns real

        function GetClickY takes nothing returns real

        function GetClickingPlayer takes nothing returns player

        function GetClickedUnit takes nothing returns unit

        function GetClickedItem takes nothing returns item

        function GetClickedDestructable takes nothing returns destructable
    
    
        function IsObjectClicked takes nothing returns boolean
            // click on the ground or a object?
        
        function IsGroundClicked takes nothing returns boolean
            // click on the ground or a object?

        function SpecialClicked takes nothing returns boolean
            // special or normal?

        function NormalClicked takes nothing returns boolean
            // special or normal?


// ForceKey Interface ( faster vJASS version)

    ForceKey.anyClick.register( trigger )   => EventReg
        // trigger will fire on click (EventReg is return value of Event)
        
    ForceKey.InitPlayer( player )           => ForceKey - Instance
        // you want to activate the forcekey mode for this player sometime
        // so first of all initialize the player
        
    // Event responses
        ForceKey.TrigX                          => real        
            
        ForceKey.TrigY                          => real         
            
        ForceKey.TrigPlayer                     => player       

        ForceKey.TrigUnit                       => unit            

        ForceKey.TrigItem                       => item        

        ForceKey.TrigDestructable               => destructable 

        ForceKey.Object                         => boolean

        ForceKey.Special                        => boolean

        ForceKey.Normal                         => boolean

    ForceKey[ player ]                      => ForceKey - Instance
        // if you don't want to save the instance id

    Instance.forceSpecialOnce()             => nothing  // Next click will be a special click

    Instance.start()                        => nothing  // Start        ForceKey mode

    Instance.pause()                        => nothing  // Pause        ForceKey mode

    Instance.switch()                       => nothing  // Start/Pause  ForceKey mode


System Code:
JASS:
library ForceKey requires Event
// ======================================================
// =        ForceKeySystem  made by Executor            =
// ======================================================

// ======================================================
// =                      Credits                       =
// =    Azlier:    Idea                                 =
// =    Jesus4Lyf: Event                                =
// ======================================================

    //! textmacro CreateForceKeyDummy takes ID SPELL ABORT
        //! external ObjectMerger w3u hpea $ID$ uabi Apiv,$SPELL$,$ABORT$ umvh 9999 umvs -50 upgr "" ubui "" utyp tree ufoo 0 ubba 0 ubsi 0 ubdi 0 ugol 0 uibo 1 ubdg 1 ufle 0 uhpm 1000000000 uhom 1 usin 0 usid 0 uhpr 9999 ucol -5 uine 0 usnd "" ussc -50 uslz -50 umdl "None.mdl" ushu "" usca 0 uaen 0 uacq 99999 unam "   " unsf "Force Key Dummy" utip "  " utub "  "
    //! endtextmacro
    //! textmacro CreateForceKeyDummyAbility takes ID, HOTKEY
        //! external ObjectMerger w3a Asdg $ID$ aart "Textures\Black32" auar "Textures\Black32" ansf "" anam "ForceKeyNormal" ahky $HOTKEY$ atar 1 "none" aut1 1 "" auu1 1 "" atp1 1 "" aub1 1 ""  aran 1 99999
    //! endtextmacro
    //! textmacro CreateForceKeyDummySpecial takes ID, HOTKEY
        //! external ObjectMerger w3a Asds $ID$ aart "Textures\Black32" auar "Textures\Black32" ansf "" anam "ForceKeySpecial" ahky $HOTKEY$ atar 1 "tree,ground,bridge,decoration,item,structure,debris,invulnerable,vulnerable,debris,wall,ward" aut1 1 "" auu1 1 "" atp1 1 "" aub1 1 ""  aran 1 99999
    //! endtextmacro
    //! textmacro CreateForceKeyDummyAbortAbility takes ID
        //! external ObjectMerger w3a Afsh $ID$ flk5 1 0 flk2 1 0 flk4 1 0 flk1 1 0 flk3 1 0 areq "" arac creeps aare 1 0 abpx 3 abpy 2 aart "ReplaceableTextures\CommandButtons\BTNCancel.blp" anam "ForceKeyCancel" atp1 1 "Abort" aub1 1 ""
    //! endtextmacro
struct  ForceKey
    // if you want to modify the hotkey used, modify the 2 lines beneath + the var HOTKEY
    // if you want to modify the ability rawcodes don't forget to modify also
    // "CreateForceKeyDummy", aswell as the 3 vars beneath.
    //! runtextmacro CreateForceKeyDummySpecial("mfks","T") 
    //! runtextmacro CreateForceKeyDummyAbility("mfka","T")
    //! runtextmacro CreateForceKeyDummyAbortAbility("mfkc")
    //! runtextmacro CreateForceKeyDummy("mfkd","mfka","mfkc")
    
    private static constant boolean     INVERT_ABILITIES    = false
    
    private static          string      HOTKEY          = "T"
    private static          integer     ORDER           = 852040
    private static          integer     NORMAL_ABIL     = 'mfka'
    private static          integer     SPECIAL_ABIL    = 'mfks'
    private static constant integer     DUM_ID          = 'mfkd'
    private static constant real        DUM_X           = 0.
    private static constant real        DUM_Y           = 0.
    private static constant real        PERIOD          = 0.05
    
    public  static          player      TrigPlayer      = null
    public  static          real        TrigX           = 0.
    public  static          real        TrigY           = 0.
    public  static          boolean     Special         = false
    public  static          boolean     Normal          = false
    public  static          boolean     Object          = false
    
    public  static          Event       NormalClick     = 0
    public  static          Event       SpecialClick    = 0
    public  static          Event       anyClick        = 0
    
    private static          timer       Timer           = CreateTimer()
    private static          trigger     GroundOrder     = CreateTrigger()
    private static          trigger     ObjectOrder     = CreateTrigger()
    private static          hashtable   Typecaster      = InitHashtable()
    private static          thistype    front           = -1
    private static          thistype    back            = -1
    private static          thistype array              Reference
    
    private                 unit        dummy 
    private                 boolean     active 
    private                 boolean     reset
    private                 player      user 
    private                 thistype    next
    private                 thistype    last 
    private                 boolean     special
    
    static method InitPlayer takes player p returns thistype
        local thistype this = thistype.allocate()
        
        set .dummy          = CreateUnit(p, DUM_ID, DUM_X, DUM_Y, 0.)
        set .user           = p
        set .active         = false
        set .next           = -1
        set .last           = -1
        set .active         = false
        set .reset          = false
        set .special        = false
        set .NormalClick    = Event.create()
        set .SpecialClick   = Event.create()
        
        set .Reference[GetPlayerId(p)] = this
        
        if ForceKey.INVERT_ABILITIES then
            call UnitRemoveAbility(.dummy, .SPECIAL_ABIL)
            call UnitAddAbility(.dummy, .NORMAL_ABIL)        
        endif
        
        call UnitRemoveAbility(.dummy, 'Amov')
        call SetUnitUserData(.dummy, this)
        call ShowUnit(.dummy, false)
        call TriggerRegisterUnitEvent(GroundOrder, .dummy, EVENT_UNIT_ISSUED_POINT_ORDER)
        call TriggerRegisterUnitEvent(ObjectOrder, .dummy, EVENT_UNIT_ISSUED_TARGET_ORDER)
        return this
    endmethod
    
    method start takes nothing returns nothing
        if .active then
            return
        endif
        set .active = true
        
        if .back == -1 then
            set .back = this
            set .front = this
        else
            set .back.next = this
            set .last      = .back
            set .back      = this
        endif        
        
        if .front == this then
            call TimerStart(.Timer, .PERIOD, true, function thistype.TimerEx)
        endif
            
        call ShowUnit(.dummy, true)
        if GetLocalPlayer() == .user then
            call SelectUnit(.dummy, true)
            call EnableSelect(false, false)
        endif
    endmethod
    
    method pause takes nothing returns nothing
        if not .active then
            return
        endif
        set .active = false
        
        if this == .front then
            set .front = .next
        else
            set .last.next = .next
        endif
        if this == .back then   
            set .back = .last
        else
            set .next.last = .last
        endif        
        
        if .front == -1 then
            call PauseTimer(.Timer)
        endif
        
        set .next = -1
        set .last = -1
        
        call ShowUnit(.dummy, false)    
        if GetLocalPlayer() == .user then
            call EnableSelect(true, true)
        endif
    endmethod
    
    method switch takes nothing returns nothing
        if .active then
            call .pause()
        else
            call .start()
        endif
    endmethod
    
    method forceSpecialOnce takes nothing returns nothing
        set .reset      = true
        set .special    = true
        call UnitRemoveAbility(.dummy, .NORMAL_ABIL )
        call UnitAddAbility(.dummy,.SPECIAL_ABIL )
    endmethod
    
    private static method ObjectOrderEx takes nothing returns boolean
        local thistype this = GetUnitUserData( GetTriggerUnit() )
        
        if not (GetIssuedOrderId() == .ORDER) then
            return false
        endif
        
        set .Normal     = not .special
        set .Special    = .special
                        
        if .reset then
            set .special = false
            set .reset   = false
            call UnitAddAbility(.dummy, .NORMAL_ABIL)
            call UnitRemoveAbility(.dummy, .SPECIAL_ABIL)
        endif
        
        call PauseUnit( .dummy, true)
        call IssueImmediateOrder(.dummy, "stop")
        call PauseUnit( .dummy, false)
        
        if GetLocalPlayer() == .TrigPlayer then
            call ForceUIKey(.HOTKEY)
        endif
        
        set .TrigPlayer = .user
        set .Object     = true
        call SaveAgentHandle(.Typecaster,0,0, GetOrderTarget())
        call .anyClick.fire() 
        if .Special then
            call .SpecialClick.fire()
        else
            call .NormalClick.fire()
        endif
        
        return false
    endmethod
    
    private static method GroundOrderEx takes nothing returns boolean
        local thistype this = GetUnitUserData( GetTriggerUnit() )
        local integer OrderID = GetIssuedOrderId()
        
        if not (OrderID == .ORDER) then
            return false
        endif
        
        set .Normal     = not .special
        set .Special    = .special 
                
        if .reset then
            set .reset      = false
            set .special    = false
            call UnitAddAbility(.dummy, .NORMAL_ABIL)
            call UnitRemoveAbility(.dummy, .SPECIAL_ABIL)
        endif
    
        call PauseUnit( .dummy, true)
        call IssueImmediateOrder(.dummy, "stop")
        call PauseUnit( .dummy, false)
        
        if GetLocalPlayer() == .TrigPlayer then
            call ForceUIKey(.HOTKEY)
        endif
      
        set .TrigPlayer = .user
        set .TrigX      = GetOrderPointX()
        set .TrigY      = GetOrderPointY()
        set .Object     = false
        call .anyClick.fire() 
        if .special then
            call .SpecialClick.fire()
        else
            call .NormalClick.fire()
        endif
        
        return false
    endmethod
    
    private static method TimerEx takes nothing returns nothing
        local thistype this = .front
        
        loop
            exitwhen this == -1
            
            if GetLocalPlayer() == .user then
                call ForceUIKey(.HOTKEY)
                call SelectUnit(.dummy, true)
            endif
            
            set this = .next
        endloop
            
    endmethod
    
    static method operator [] takes player p returns thistype
        return .Reference[GetPlayerId(p)]
    endmethod
    
    static method operator TrigUnit takes nothing returns unit
        return LoadUnitHandle(.Typecaster,0,0)
    endmethod
    
    static method operator TrigItem takes nothing returns item
        return LoadItemHandle(.Typecaster,0,0)
    endmethod
    
    static method operator TrigDestructable takes nothing returns destructable
        return LoadDestructableHandle(.Typecaster,0,0)
    endmethod
    
    
    private static method onInit takes nothing returns nothing
        local integer   i   = .NORMAL_ABIL
        if ForceKey.INVERT_ABILITIES then
            set .NORMAL_ABIL    = .SPECIAL_ABIL
            set .SPECIAL_ABIL   = i
        endif
        set .front          = -1
        set .back           = -1
        set .anyClick       = Event.create()
        call TriggerAddCondition(.GroundOrder, Condition(function thistype.GroundOrderEx))
        call TriggerAddCondition(.ObjectOrder, Condition(function thistype.ObjectOrderEx))
    endmethod
    
    private method onDestroy takes nothing returns nothing
        // A ForceKey Struct is not meant to be destroyed but if you want ..
        call .pause()
        call RemoveUnit( .dummy )
    endmethod
endstruct

public function TriggerRegisterForceKeyClickEvent takes trigger t returns EventReg
    return ForceKey.anyClick.register( t )
endfunction

public function EnableForceKeyForPlayer takes player p returns nothing
    local ForceKey F = ForceKey[p]
    if F == 0 then
        call ForceKey.InitPlayer(p).start()
        return
    endif
    call F.start()
endfunction

public function DisableForceKeyForPlayer takes player p returns nothing
    call ForceKey[p].pause()
endfunction

public function ForceSpecialClick takes player p returns nothing
    call ForceKey[p].forceSpecialOnce()
endfunction

public function GetClickX takes nothing returns real
    return ForceKey.TrigX
endfunction

public function GetClickY takes nothing returns real
    return ForceKey.TrigY
endfunction

public function GetClickPlayer takes nothing returns player
    return ForceKey.TrigPlayer
endfunction

public function GetClickedUnit takes nothing returns unit
    return ForceKey.TrigUnit
endfunction

public function GetClickedItem takes nothing returns item
    return ForceKey.TrigItem
endfunction

public function GetClickedDestructable takes nothing returns destructable
    return ForceKey.TrigDestructable
endfunction

public function IsObjectClicked takes nothing returns boolean
    return ForceKey.Object
endfunction

public function IsGroundClicked takes nothing returns boolean
    return not ForceKey.Object
endfunction

public function SpecialClicked takes nothing returns boolean
    return ForceKey.Special
endfunction

public function NormalClicked takes nothing returns boolean
    return ForceKey.Normal
endfunction

endlibrary


Example:

JASS:
scope ForceKeyUse initializer INIT
    globals 
        private player      PLAYER      = Player(0)
    endglobals
    
    private function onClickEx takes nothing returns boolean
        local string s = ""
        
        if ForceKey.Special then
            set s = "special-"
        else
            set s = "normal-"
        endif
        
        if ForceKey.Object then 
            if ForceKey.TrigUnit != null then
                call BJDebugMsg("JASS ForceKey: You "+s+"clicked " + GetUnitName(ForceKey.TrigUnit))
            elseif ForceKey.TrigDestructable != null then
                call BJDebugMsg("JASS ForceKey: You "+s+"clicked " + GetDestructableName(ForceKey.TrigDestructable))
            elseif ForceKey.TrigItem != null then
                call BJDebugMsg("JASS ForceKey: You "+s+"clicked " + GetItemName(ForceKey.TrigItem))
            else
                call BJDebugMsg("Unknown Object")
            endif
            return false
        endif
        call BJDebugMsg("JASS ForceKey: You "+s+"clicked " + R2S(ForceKey.TrigX) + "/" + R2S(ForceKey.TrigY))
        return false
    endfunction
    
    private function onChatEx takes nothing returns boolean
        call ForceKey[PLAYER].switch()
        return false
    endfunction

    private function EscEx takes nothing returns boolean
        call ForceKey[PLAYER].forceSpecialOnce()
        return true
    endfunction
    
    private function INIT takes nothing returns nothing
        local trigger onChat    = CreateTrigger()
        local trigger onClick   = CreateTrigger()
        local trigger onEsc     = CreateTrigger()
        
        // Force a "special click" by hitting escape
        call TriggerRegisterPlayerEvent(onEsc, PLAYER, EVENT_PLAYER_END_CINEMATIC)
        call TriggerAddCondition(onEsc,Condition(function EscEx))
        
        // Enable and disable the mode by typing "-StartJASS" and "-StopJASS"
        call TriggerRegisterPlayerChatEvent(onChat, Player(0), "-StartJASS", true)
        call TriggerRegisterPlayerChatEvent(onChat, Player(0), "-StopJASS", true)
        call TriggerAddCondition(onChat , Condition( function onChatEx))
        
        // On click ...
        call ForceKey.InitPlayer(PLAYER)
        call ForceKey.anyClick.register( onClick )
        call TriggerAddCondition( onClick, Condition( function onClickEx ) )
                
                
        // some other stuff
        call BJDebugMsg("Use '-StartJASS','-StopJASS', the escape and your left mouse button to test my sys")
        set onChat = null
        set onClick = null
    endfunction
endscope

[/SPOILER]

Changelog:
11-06-2009 Release v1.0
11-13-2009 Big Update: +Special +Design enhanced
11-14-2009 Update: Recoded few things

Credits to
  • Azlier: Idea
  • Jesus4Lyf: Event

Criticism is welcome.

Greetings.
 

Attachments

  • ForceKey.w3x
    41.4 KB · Views: 261

Jesus4Lyf

Good Idea™
Reaction score
397
Aw, this was so Azlier's. :p

But cool. You should detect when (right click OR esc) happens. Don't give an X/Y for it, but you can tell. Would be great for "reload" your gun or something.

So basically, spam "S" or something, and if a unit is issued stop, then issue hotkey for your special thing, and while the targetter is up of course the stop hotkey will not work... Hence right click/esc detection. :)
 

Executor

I see you
Reaction score
57
Aw, this was so Azlier's. :p

But cool. You should detect when (right click OR esc) happens. Don't give an X/Y for it, but you can tell. Would be great for "reload" your gun or something.

So basically, spam "S" or something, and if a unit is issued stop, then issue hotkey for your special thing, and while the targetter is up of course the stop hotkey will not work... Hence right click/esc detection. :)

Ahh .. I think I have a good idea!! ty .. let's see if it works.
 

Azlier

Old World Ghost
Reaction score
461
I suggest setting the dummies' flying height to something obscenely high, like 10000. That way they can't be box selected.
 

Executor

I see you
Reaction score
57
Good idea, done. (when updating)

In moment I have the following situation:
I added a second ability based on another ability to the unit. The timer forces first the special (second) hotkey. When an order is issued the first hotkey is forced.

The idea is:
If the user presses rightclick (cancels target selection), the timer forces second hotkey. If the user continous leftclick he will always use first ability.

The problem:
The timer seems to dominate even when the OrderIssuedEx function instantly forces the first hotkey.
 

Weep

Godspeed to the sound of the pounding
Reaction score
400
It's a bit unsightly how the unit buttons flicker visible for a moment when you click...
 

Azlier

Old World Ghost
Reaction score
461
Yeah, I got around that by making the dummy a building in my latest build. Or something. I did something.
 

Azlier

Old World Ghost
Reaction score
461
But that ruins the icons. Making it a building is much cleaner and lets you switch between click mode and normal WC3 mode without many, if any, noticeable differences in WC3 mode.
 

Executor

I see you
Reaction score
57
y, also had the building type to hide the icons, but somehow since I use the ObjectMerger to generate the dummy the icons remain.

But as I finished my last test 2,5h ago I will have more time from now on to manage these things ;)
 

Hatebreeder

So many apples
Reaction score
381
I was thinking: If you are able to redect where you clicked, then how about thinking about a function/library that allows you "get" the multiboarditem of the clicked multiboard?

I don't really understand how this works since I didn't check the Code, but would it be possible to "get" those values just by detecting the click?
 

Executor

I see you
Reaction score
57
Ye I also thought about that, but .. I think you cannot cast on mbs :p

But it would really be a great idea to create a library, able to convert map coordinates + camera to screen coordinates and otherwise.

*Updated*
(posted code only [objectmerger settings only])
- Buttons not visible any longer
- No standard unit buttons (move etc.)
- Dummy not selectable anymore
 

Azlier

Old World Ghost
Reaction score
461
I believe that my newest version of click detection beats this one.
 

Attachments

  • Azzy's Click Detection.w3x
    22.3 KB · Views: 220

Executor

I see you
Reaction score
57
Well you focus enabling object selection why I prefer being able to click anywhere on the ground (even when cliffs don't work somehow for me :)).
I wanted to introduce this object targetability as an alternative method later on.

But I also have some criticism:
What about unit groups with the hotkeys [1,2,3,4....]. If the player uses this method to select multiple units you won't deselect them.
And atm it isn't MUI, which simplifies you sys heavily in coding.
 

Azlier

Old World Ghost
Reaction score
461
Yes, mine isn't MPI at the moment. However, if I fix that, it's quite good :p.

I can fix the selection thing.
 

Executor

I see you
Reaction score
57
Well after all that fixes the only difference between our systems will be that yours is able to target objects while mine targets through them.
 

Troll-Brain

You can change this now in User CP.
Reaction score
85
Hmm, i think it's time to say that you can do that this simply with calling EnableSelect( false, false ).
I bet it can be used safely with GetLocalPlayer.

And use a dummy peasant, or at least a passive ability which can target trees. (if you want ofc)
(You can select it by a trigger action after the EnableSelect), on right click he should be able to target all widgets.
And you can change the icon if you want.

But then i'm agree it will be a right click instead of a left click.
Anyway, you can keep your stuff except there is absolulety no need to select the dummy all the time, since you can forbid selection for players.
 

Azlier

Old World Ghost
Reaction score
461
Hmm. This seems to do the job.

JASS:
library ClickDetect initializer Init requires Event

globals
    private constant integer DUMMY_ID = 'cont'
    private constant integer TARGET_ID = 'A001'
    
    private constant string TARGET_HOTKEY = "P"
    private constant integer STOP_ORDER_ID = 0xD0004
    
    private constant integer TARGET_ORDER_ID = 0xD0048
endglobals

globals
    private Event LClickPointEv
    private Event LClickObjEv
    private unit array Controller
    private real CliX = 0
    private real CliY = 0
    
    private widget TObject = null
    private hashtable Tht = InitHashtable()
    private timer T = CreateTimer()
    
    private integer On = 0
    private boolean array Enabled
endglobals

function CD_RegisterClickPoint takes trigger t returns nothing
    call LClickPointEv.register(t)
endfunction

function CD_RegisterClickObject takes trigger t returns nothing
    call LClickObjEv.register(t)
endfunction

private function Update takes nothing returns nothing
    local integer i = 11
    loop
        if Enabled<i> then
            call SelectUnit(Controller<i>, true)
            call ForceUIKey(TARGET_HOTKEY)
        endif
        exitwhen i == 0
        set i = i - 1
    endloop
endfunction

function EnableClicking takes player which, boolean enable returns nothing
    local integer i = GetPlayerId(which)
    if enable then
        call TimerStart(T, 0.005, true, function Update)
        set On = On + 1
        if GetLocalPlayer() == which then
            call ClearSelection()
            call EnableSelect(false, false)
            call SelectUnit(Controller<i>, true)
        endif
        set Enabled<i> = true
    else
        if GetLocalPlayer() == which then
            call SelectUnit(Controller<i>, false)
            call EnableSelect(true, true)
        endif
        set On = On - 1
        if On &lt; 1 then
            call PauseTimer(T)
            set On = 0
        endif
        set Enabled<i> = false
    endif
endfunction

function GetClickedWidget takes nothing returns widget
    return TObject
endfunction

function GetClickedUnit takes nothing returns unit
    return LoadUnitHandle(Tht, 0, 0)
endfunction

function GetClickedItem takes nothing returns item
    return LoadItemHandle(Tht, 0, 0)
endfunction

function GetClickedDestructable takes nothing returns destructable
    return LoadDestructableHandle(Tht, 0, 0)
endfunction

private function LClickPoint takes nothing returns boolean
    local integer i
    if GetIssuedOrderId() == TARGET_ORDER_ID then
        set CliX = GetOrderPointX()
        set CliY = GetOrderPointY()
        call LClickPointEv.fire()
        set i = GetPlayerId(GetOwningPlayer(GetTriggerUnit()))
        call PauseUnit(Controller<i>, true)
        call IssueImmediateOrderById(Controller<i>, STOP_ORDER_ID)
        call PauseUnit(Controller<i>, false)
    endif
    return false
endfunction

private function LClickObject takes nothing returns boolean
    local integer i
    if GetIssuedOrderId() == TARGET_ORDER_ID then
        set TObject = GetOrderTarget()
        call SaveWidgetHandle(Tht, 0, 0, TObject)
        call LClickObjEv.fire()
        call SaveWidgetHandle(Tht, 0, 0, null)
        set TObject = null
        set i = GetPlayerId(GetOwningPlayer(GetTriggerUnit()))
        call PauseUnit(Controller<i>, true)
        call IssueImmediateOrderById(Controller<i>, STOP_ORDER_ID)
        call PauseUnit(Controller<i>, false)
    endif
    return false
endfunction

private function Init takes nothing returns nothing
    local trigger t1 = CreateTrigger()
    local trigger t2 = CreateTrigger()
    local integer i = 11
    loop
        set Controller<i> = CreateUnit(Player(0), DUMMY_ID, 0, 0, 270)
        call SelectUnit(Controller<i>, true)
        call SetUnitFlyHeight(Controller<i>, 100000, 0)
        call UnitRemoveAbility(Controller<i>, &#039;Amov&#039;)
        call UnitRemoveAbility(Controller<i>, &#039;Aatk&#039;)
        exitwhen i == 0
        set i = i - 1
    endloop
    set LClickPointEv = Event.create()
    set LClickObjEv = Event.create()
    
    set i = 11
    loop
        call TriggerRegisterUnitEvent(t1, Controller<i>, EVENT_UNIT_ISSUED_POINT_ORDER)
        call TriggerRegisterUnitEvent(t2, Controller<i>, EVENT_UNIT_ISSUED_TARGET_ORDER)
        exitwhen i == 0
        set i = i - 1
    endloop
    call TriggerAddCondition(t1, Condition(function LClickPoint))
    call TriggerAddCondition(t2, Condition(function LClickObject))
endfunction

endlibrary</i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i>
 

Executor

I see you
Reaction score
57
Well as soon as I discover why how I prevent my dummy from executing the suicide command I will update mine to compare it.

*Updated*

Edit: does someone know a spell not equal to kaboom which allows targeting trees but also anything else (like cliffs, units etc.)? Otherwise I have to recode a few things.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top