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

[Snippet] IsUnitChanneling

This is a small and clear resource that detects if a unit is channeling or not.

JASS:
/**************************************
*
*   IsUnitChanneling
*   v2.1.0.0
*   By Magtheridon96
*
*   - Tells whether a unit is channeling or not.
*
*   Requirements:
*   -------------
*
*       - RegisterPlayerUnitEvent by Magtheridon96
*           - hiveworkshop.com/forums/jass-resources-412/snippet-registerplayerunitevent-203338/
*
*       Optional:
*       ---------
*
*           - UnitIndexer by Nestharus
*               - hiveworkshop.com/forums/jass-resources-412/system-unit-indexer-172090/
*           - Table by Bribe
*               - hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*
*   API:
*   ----
*
*       - function IsUnitChanneling takes unit whichUnit returns boolean
*           - Tells whether a unit is channeling or not.
*
*       - function IsUnitChannelingById takes integer unitIndex returns boolean
*           - Tells whether a unit is channeling or not given the unit index.
*             (This function is only available if you have UnitIndexer)
*
**************************************/
library IsUnitChanneling requires optional UnitIndexer, optional Table, RegisterPlayerUnitEvent
    
    private struct OnChannel extends array
        static if LIBRARY_UnitIndexer then
            static boolean array channeling
        else
            static if LIBRARY_Table then
                static key k
                static Table channeling = k
            else
                static hashtable hash = InitHashtable()
            endif
        endif
        
        private static method onEvent takes nothing returns nothing
            static if LIBRARY_UnitIndexer then
                local integer id = GetUnitUserData(GetTriggerUnit())
                set channeling[id] = not channeling[id]
            else
                static if LIBRARY_Table then
                    local integer id = GetHandleId(GetTriggerUnit())
                    set channeling.boolean[id] = not channeling.boolean[id]
                else
                    local integer id = GetHandleId(GetTriggerUnit())
                    call SaveBoolean(hash, 0, id, not LoadBoolean(hash, 0, id))
                endif
            endif
        endmethod
        
        private static method onInit takes nothing returns nothing
           call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_CHANNEL, function thistype.onEvent)
           call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_ENDCAST, function thistype.onEvent)
        endmethod
    endstruct
    
    static if LIBRARY_UnitIndexer then
        function IsUnitChannelingById takes integer id returns boolean
            return OnChannel.channeling[id]
        endfunction
    endif
    
    function IsUnitChanneling takes unit u returns boolean
        static if LIBRARY_UnitIndexer then
            return OnChannel.channeling[GetUnitUserData(u)]
        else
            static if LIBRARY_Table then
                return OnChannel.channeling.boolean[GetHandleId(u)]
            else
                return LoadBoolean(OnChannel.hash, 0, GetHandleId(u))
            endif
        endif
    endfunction
    
endlibrary

Vanilla JASS Version

JASS:
//**************************************
//
//  IsUnitChanneling (Vanilla JASS)
//  v2.1.0.0
//  By Magtheridon96
//
//  - Tells whether a unit is channeling or not.
//
//  Requirements:
//  -------------
//
//      - RegisterPlayerUnitEvent by Magtheridon96 (Vanilla JASS version)
//          - hiveworkshop.com/forums/jass-resources-412/snippet-registerplayerunitevent-203338/
//
//  Global Variables:
//  -----------------
//
//      hashtable UnitChannelTable
//
//  Trigger Name:
//  -------------
//
//      IsUnitChanneling
//
//  API:
//  ----
//
//      - function IsUnitChanneling takes unit whichUnit returns boolean
//          - Tells whether a unit is channeling or not.
//
//**************************************
function IsUnitChanneling_onEvent takes nothing returns nothing
    local integer id = GetHandleId(GetTriggerUnit())
    call SaveBoolean(udg_UnitChannelTable, 0, id, not LoadBoolean(udg_UnitChannelTable, 0, id))
endfunction

function IsUnitChanneling takes unit u returns boolean
    return LoadBoolean(udg_UnitChannelTable, 0, GetHandleId(u))
endfunction

function InitTrig_IsUnitChanneling takes nothing returns nothing
    set udg_UnitChannelTable = InitHashtable()
    call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_CHANNEL, function IsUnitChanneling_onEvent)
    call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_ENDCAST, function IsUnitChanneling_onEvent)
endfunction

Feel free to comment..
 
Last edited:
Level 7
Joined
Oct 11, 2008
Messages
304
// Because I know a lot of you are still using my old library ;) \o/ (I'm one of them).

Also, why:

JASS:
local code c = function thistype.onDeindex
call UnitIndexer.DEINDEX.register(Filter(c))

/* instead of */

call UnitIndexer.DEINDEX.register(Filter(function thistype.onDeindex))

Edit: Err, nevermind about it ^_^, I remember now the return thing.
 
Level 12
Joined
Feb 22, 2010
Messages
1,115
I am sorry if I am mistaken, but I don't understand why are you catching orders or dealing with timers.

JASS:
library IsUnitChanneling initializer Init requires RegisterPlayerUnitEvent

globals
    private group Casters = CreateGroup()
endglobals

private function Add takes nothing returns nothing
    call GroupAddUnit(Casters, GetTriggerUnit())
endfunction

private function Remove takes nothing returns nothing
    call GroupRemoveUnit(Casters, GetTriggerUnit())
endfunction

private function Init takes nothing returns nothing
    call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_CAST, function Add)
    call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_ENDCAST, function Remove)
endfunction

function IsUnitChanneling takes unit u returns boolean
    return IsUnitInGroup(u, Casters)
endfunction

endlibrary
 
ENDCAST runs when a spell finishes channeling?

edit
I only used those because I didn't know in what order the events START_CHANNEL and ISSUED_ORDER run.
If you can confirm to me that ENDCAST runs when a unit finishes channeling, I will update this.

Also, you shouldn't be used groups :p
A simple boolean array would be lighter and if you don't want UnitIndexer, I could make it optional and use a Table instead ^.^
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Dude at this rate you just suck it up and use a dummy "return false":

JASS:
local code c = function thistype.onDeindex
call UnitIndexer.DEINDEX.register(Filter(c))

//Compare the lines of text:

local code c = 
return false

boolean
nothing

You're just overcomplicating it ;)

And I agree with Ceday, I think he's right in fact.
 
START_CHANNEL and END_CAST it is.

edit

ANSWER ME NAO.

JASS:
/**************************************
*
*   IsUnitChanneling
*   v1.1.0.0
*   By Magtheridon96
*
*   - Tells whether a unit is channeling or not.
*
*   Requirements:
*	-------------
*
*   	- RegisterPlayerUnitEvent by Magtheridon96
*   		- hiveworkshop.com/forums/jass-resources-412/snippet-registerplayerunitevent-203338/
*
*	Optional Requirements:
*	----------------------
*
* 		- UnitIndexer by Nestharus
*       	- hiveworkshop.com/forums/jass-resources-412/system-unit-indexer-172090/
*		- Table by Bribe
*			- hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*
*	Note:
*	-----
*
*		- You can have either UnitIndexer or Table.
*		  Having both is ok too, but the system will use UnitIndexer's functions instead.
*		  Having none also works. The system will just use a simple Hashtable in this case.
*
*   API:
*   ----
*
*     	- function IsUnitChanneling takes unit whichUnit returns boolean
*  	    	- Tells whether a unit is channeling or not.
*
**************************************/
library IsUnitChanneling requires optional UnitIndexer, optional Table, RegisterPlayerUnitEvent
    
	private struct OnChannel extends array
		static if LIBRARY_UnitIndexer then
		    static boolean array channeling
		elseif LIBRARY_Table then
			private static key tb
			static Table channeling = tb
		else
			static hashtable channeling = InitHashtable()
		endif
        
        private static method onStart takes nothing returns nothing
        	static if LIBRARY_UnitIndexer then
		        set channeling[GetUnitUserData(GetTriggerUnit())] = true
		    elseif LIBRARY_Table then
		    	set channeling.boolean[GetHandleId(GetTriggerUnit())] = true
		    else
		    	call SaveBoolean(channeling, GetHandleId(GetTriggerUnit()), 0, true)
		    endif
		endmethod
        
        private static method onEnd takes nothing returns nothing
        	static if LIBRARY_UnitIndexer then
	            set channeling[GetUnitUserData(GetTriggerUnit())] = false
	        elseif LIBRARY_Table then
	        	set channeling.boolean[GetHandleId(GetTriggerUnit())] = false
	        else
	        	call SaveBoolean(channeling, GetHandleId(GetTriggerUnit()), 0, false)
	        endif
        endmethod
        
        static if LIBRARY_UnitIndexer then
	        private static method onDeindex takes nothing returns nothing
	   	 		set channeling[GetIndexedUnitId()] = false
        	endmethod
        endif
        
        private static method onInit takes nothing returns nothing
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_CHANNEL, function thistype.onStart)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_END_CAST, function thistype.onEnd)
            static if LIBRARY_UnitIndexer then
	            call UnitIndexer.DEINDEX.register(Condition(function thistype.onDeindex))
	        endif
        endmethod
    endstruct
    
    function IsUnitChanneling takes unit u returns boolean
    	static if LIBRARY_UnitIndexer then
	        return OnChannel.channeling[GetUnitUserData(u)]
	    elseif LIBRARY_Table then
	    	return OnChannel.channeling.boolean[GetHandleId(u)]
	    else
	    	return LoadBoolean(OnChannel.channeling, GetHandleId(u), 0)
	    endif
    endfunction
    
endlibrary

Does this compile? ^.^
I need to know if it compiles with:
- UnitIndexer
- Table
- Both UnitIndexer and Table
- Nothing
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
This way you save hash memory from removed units:

JASS:
    @set channeling.boolean[GetHandleId(GetTriggerUnit())] = false@
    call channeling.boolean.remove(GetHandleId(GetTriggerUnit()))
else
    @call SaveBoolean(channeling, GetHandleId(GetTriggerUnit()), 0, false)@
    call RemoveSavedBoolean(channeling, GetHandleId(GetTriggerUnit()), 0)
 

BBQ

BBQ

Level 4
Joined
Jun 7, 2011
Messages
97
This doesn't work properly—and there's no way you can make it work properly.

Translate "properly" to "correctly in every imaginable situation".

Well, obviously, there is a way, but it "would be too costly".
 

BBQ

BBQ

Level 4
Joined
Jun 7, 2011
Messages
97
A unit that is channelling a spell can cast some other spells while remaining uninterrupted, such as Berserk and Wind Walk. Normally, that isn't a problem, because those spells ignore the cast point of the unit (so the two events you're using trigger right after another), but it becomes one when those spells are given a casting time.

In that case, if, say, a unit is casting a 6-second channelling spell and goes on to cast a Wind Walk with a 3-second casting time mid-channeling, then during those 3 seconds your snippet will return false, even though it should most definitely return true, as the channelling is uninterrupted.

Also, why are you using the UNIT_SPELL_CHANNEL event? That one starts when the unit begins the animation of the spell (regardless if the spell is channelling or not), so it can end up with inaccurate result even more often (due to the fact that units can "animation-cancel" their spells without wasting cooldown or mana and without the spell have its effect at all). This is especially a problem for units with a long casting point.
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
Then what do you propose I should use? :/
The only other event I can come up with for this is EVENT_PLAYER_UNIT_SPELL_CAST, but that doesn't seem right D:
Is it?

edit
OR, should I use both events to verify that a unit is channeling? :eek:

a question, if u use finishing spell/ stoped casting or something like this, what happen if unit will be replaced during channeling (like if somebody type a load code for load another hero)?
 

BBQ

BBQ

Level 4
Joined
Jun 7, 2011
Messages
97
Then what do you propose I should use? :/
The only other event I can come up with for this is EVENT_PLAYER_UNIT_SPELL_CAST, but that doesn't seem right D:
Is it?

edit
OR, should I use both events to verify that a unit is channeling? :eek:

You should use a combination SPELL_CHANNEL, SPELL_EFFECT and SPELL_ENDCAST.

Note that it still won't fix the problem with Wind Walk, Berserk and the likes. But that's rare. Anitarf's SpellEvent has the same problem as well.
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
I tested that before I made this.
The stop channel event will fire correctly.

i tryed too similiar in jass but at "turn off indexer, replace hero, turn back indexer" my trigger needed a fix, i wanted try this too , after i copyed to we jngp i noticed still i must import alot library so overall far more complex than look like when i watched 1st post (i just wondered how that short) :D
 
Level 12
Joined
Feb 22, 2010
Messages
1,115
Here is the version that prevents things like windwalk and berserk cause problem.

Sorry if there are some horrible coding inside it, I needed something quick for a friend's AI and I am sure u can fix easy.

Maybe need to add things like immo, defend, mana shield.

Edit: This heavily fails if abilities such as ww or berserk have casting time > 0.So I think also need to do some extra checking for them.It requires multiple timers per ordered unit since we can't know before which unit's ability has casting time or not and duration difference.(I dont know about timer merging)

This is how the game execute order works

A unit issues order
A unit begins channel
(casting time here)
A unit begins cast

So it seems we also need to catch when channel event fires but begin cast event not for the spells which started with these specific orders.

Edit 2:Forget crossed parts, anyway I didn't need such thing for now normal windwalks enough =)

JASS:
library IsUnitChanneling requires optional UnitIndexer, optional Table, RegisterPlayerUnitEvent
    
    
    private struct OnChannel extends array
        static timer BLOCKT = CreateTimer()
        static integer BLOCKI = 0
        static unit array BLOCKU
        // static group BLOCKG = CreateGroup()
        static if LIBRARY_UnitIndexer then
            static boolean array channeling
            static boolean array ordering
        else
            static if LIBRARY_Table then
                static key k
                static Table channeling = k
                static key kk
                static Table ordering = kk
            else
                static hashtable hash = InitHashtable()
            endif
        endif
        
        private static method onEvent takes nothing returns nothing
            static if LIBRARY_UnitIndexer then
                local integer id = GetUnitUserData(GetTriggerUnit())
                if not ordering[id] then
                    set channeling[id] = not channeling[id]
                endif
            else
                static if LIBRARY_Table then
                    local integer id = GetHandleId(GetTriggerUnit())
                    if not ordering.boolean[id] then
                        set channeling.boolean[id] = not channeling.boolean[id]
                    endif
                else
                    local integer id = GetHandleId(GetTriggerUnit())
                    if not LoadBoolean(hash, 1, id) then
                        call SaveBoolean(hash, 0, id, not LoadBoolean(hash, 0, id))
                    endif
                endif
            endif
        endmethod
        
        private static method ResetBlock takes nothing returns nothing
            //call GroupClear(BLOCKG)
            local integer i = 0
            loop
                exitwhen i >= BLOCKI
                
                static if LIBRARY_UnitIndexer then
                    set ordering[GetUnitUserData(BLOCKU[i])] = false
                else
                    static if LIBRARY_Table then
                        set ordering.boolean[GetHandleId(BLOCKU[i])] = false
                    else
                        call SaveBoolean(hash, 1, GetHandleId(BLOCKU[i]), false)
                    endif
                endif
                
                set BLOCKU[i] = null
                set i = i + 1
            endloop
            set BLOCKI = 0
            call PauseTimer(BLOCKT)
        endmethod
        
        private static method onEvent2 takes nothing returns nothing
            local integer order = GetIssuedOrderId()
            // 852100:berserk
            // 852129:windwalk
            if order == 852100 or order == 852129 then
                //call GroupAddUnit(BLOCKG, GetTriggerUnit())
                set BLOCKU[BLOCKI] = GetTriggerUnit()
                
                static if LIBRARY_UnitIndexer then
                    set ordering[GetUnitUserData(BLOCKU[BLOCKI])] = true
                else
                    static if LIBRARY_Table then
                        set ordering.boolean[GetHandleId(BLOCKU[BLOCKI])] = true
                    else
                        call SaveBoolean(hash, 1, GetHandleId(BLOCKU[BLOCKI]), true)
                    endif
                endif
                
                call TimerStart(BLOCKT, 0.00, false, function thistype.ResetBlock)
                set BLOCKI = BLOCKI + 1
            endif
        endmethod
        
        private static method onInit takes nothing returns nothing
           call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_CHANNEL, function thistype.onEvent)
           call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_ENDCAST, function thistype.onEvent)
           call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function thistype.onEvent2)
        endmethod
    endstruct
    
    static if LIBRARY_UnitIndexer then
        function IsUnitChannelingById takes integer id returns boolean
            return OnChannel.channeling[id]
        endfunction
    endif
    
    function IsUnitChanneling takes unit u returns boolean
        static if LIBRARY_UnitIndexer then
            return OnChannel.channeling[GetUnitUserData(u)]
        else
            static if LIBRARY_Table then
                return OnChannel.channeling.boolean[GetHandleId(u)]
            else
                return LoadBoolean(OnChannel.hash, 0, GetHandleId(u))
            endif
        endif
    endfunction
    
endlibrary


Edit 3:Lol or just this...should have thought before.

Edit 4:The lastability thing is for, when a unit click an ability with casting time multiple times.

JASS:
library IsUnitChanneling requires optional UnitIndexer, optional Table, RegisterPlayerUnitEvent
    
    
    private struct OnChannel extends array
        static if LIBRARY_UnitIndexer then
            static int array castingcount
            static int array lastability
        else
            static if LIBRARY_Table then
                static key k
                static castingcount = k
                static key kk
                static lastability = kk
            else
                static hashtable hash = InitHashtable()
            endif
        endif
        
        private static method onChannel takes nothing returns nothing
            static if LIBRARY_UnitIndexer then
                local integer id = GetUnitUserData(GetTriggerUnit())
                if lastability[id] != GetSpellAbilityId() then
                    set castingcount[id] = castingcount[id] + 1
                    set lastability[id] = GetSpellAbilityId()
                endif
            else
                static if LIBRARY_Table then
                    local integer id = GetHandleId(GetTriggerUnit())
                    if lastability.integer[id] != GetSpellAbilityId() then
                        set castingcount.integer[id] = castingcount.integer[id] + 1
                        set lastability.integer[id] = GetSpellAbilityId()
                    endif
                else
                    local integer id = GetHandleId(GetTriggerUnit())
                    if LoadInteger(hash, 1, id) != GetSpellAbilityId() then
                        call SaveInteger(hash, 0, id, LoadInteger(hash, 0, id) + 1)
                        call SaveInteger(hash, 1, id, GetSpellAbilityId())
                    endif
                endif
            endif
        endmethod
        
        private static method onFinish takes nothing returns nothing
            static if LIBRARY_UnitIndexer then
                local integer id = GetUnitUserData(GetTriggerUnit())
                set castingcount[id] = castingcount[id] - 1
                set lastability[id] = 0
            else
                static if LIBRARY_Table then
                    local integer id = GetHandleId(GetTriggerUnit())
                    set castingcount.integer[id] = castingcount.integer[id] - 1
                    set lastability.integer[id] = 0
                else
                    local integer id = GetHandleId(GetTriggerUnit())
                    call SaveInteger(hash, 0, id, LoadInteger(hash, 0, id) - 1)
                    call SaveInteger(hash, 1, id, 0)
                endif
            endif
        endmethod
        
        private static method onInit takes nothing returns nothing
           call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_CHANNEL, function thistype.onChannel)
           call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_ENDCAST, function thistype.onFinish)
        endmethod
    endstruct
    
    static if LIBRARY_UnitIndexer then
        function IsUnitChannelingById takes integer id returns boolean
            return OnChannel.castingcount[id] > 0
        endfunction
    endif
    
    function IsUnitChanneling takes unit u returns boolean
        static if LIBRARY_UnitIndexer then
            return OnChannel.castingcount[GetUnitUserData(u)] > 0
        else
            static if LIBRARY_Table then
                return OnChannel.castingcount.integer[GetHandleId(u)] > 0
            else
                return LoadInteger(OnChannel.hash, 0, GetHandleId(u)) > 0
            endif
        endif
    endfunction
    
endlibrary
 
Last edited:
Top