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

[vJASS] DisableAutocast

Level 17
Joined
Jun 17, 2007
Messages
1,433
Sometimes it can be useful to disable the auto-casting of certain abilities. For example, you may have a custom Searing Arrows ability that has a cooldown. Unfortunately, the auto-casting of it does not properly trigger the cooldown (and I believe has other issues relating to things like range). I haven't written vJASS in ~8 years, so maybe my approach is terrible or outdated, but it seems fine to me.

This API is simple:
JASS:
function DisableAutocast takes integer activateOrder, integer deactivateOrder returns nothing
where 'activateOrder' is the order id to enable auto-casting, and 'deactivateOrder' is the order to turn it off. For example,
JASS:
call DisableAutocast(852067, 852068)
prevents Inner Fire from being autocast.


JASS:
library DisableAutocast initializer Init requires TimerUtils, Table

globals
    private Table table
endglobals

private struct DisableData
    unit u
    integer deactivateOrder

    static method create takes unit u, integer deactivateOrder returns DisableData
        local DisableData this = DisableData.allocate()

        set this.u = u
        set this.deactivateOrder = deactivateOrder

        return this
    endmethod
endstruct

private function Callback takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local DisableData d = GetTimerData(t)
    call IssueImmediateOrderById(d.u, d.deactivateOrder)
    call ReleaseTimer(t)
    call d.destroy()

    set t = null
endfunction

private function Actions takes nothing returns nothing
    local timer t
    local DisableData d
    local integer orderId = GetIssuedOrderId()

    if table.exists(orderId) then
        set t = NewTimer()
        set d = DisableData.create(GetTriggerUnit(), table[orderId])
        call SetTimerData(t, d)
        call TimerStart(t, 0., false, function Callback)
        set t = null
    endif
endfunction

function DisableAutocast takes integer activateOrder, integer deactivateOrder returns nothing
    set table[activateOrder] = deactivateOrder
endfunction

public function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_ORDER)
    call TriggerAddAction(t, function Actions)
    set table = Table.create()
endfunction

endlibrary
 

Jampion

Code Reviewer
Level 15
Joined
Mar 25, 2016
Messages
1,327
You should include some documentation, especially some information and links about the required libraries, even if they are common ones in this case.
Proper documentation must be included with all scripts. This includes, but is not limited to:
  • Library requirements. (This includes but is not limited to the name of the library, the author, and a link to the library's thread.)
  • A listing of the public API. (This includes functions, structs, modules, globals, struct members, function interfaces, and so on.)
  • Instructions on how to import the script into a map should it not be simply copy and paste.
  • A clear description on how the script should be used and, if possible, explain why the resource is useful.
The table library you are using is outdated. You should use this one: [Snippet] New Table
It has a backwards compatibility snippet, but for new resources one should use the new table.

It would be nice to be able to use order strings as well. You could follow the JASS naming convention for orders and have DisableAutocast and DisableAutocastById.


There are 2 side effects when you use this. The unit is being stopped and all its orders are canceled. Usually toggling autocast is instant and does not interrupt orders.
Other currently active autocast effects are turned off.

Does this have use outside of arrow abilities? Other autocast abilities can usually be emulated by using some non-autocast ability and a dummy ability to apply the effect.

(and I believe has other issues relating to things like range)
Arrow abilities allow you to attack from further away, if you cast them manually and cast range is greater than the attack range of the unit.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Here's the most simplified version possible
JASS:
library DisableAutocast requires Table

globals
    private Table table = 0
    private integer n = 0
    private unit array units
    private integer array deactivateOrder
    private timer tick = CreateTimer()
endglobals

private function Callback takes nothing returns nothing
    local integer i = 0
    loop
        exitwhen i == n
        call IssueImmediateOrderById(units[i], deactivateOrder[i])
        set i = i + 1
    endloop
    set n = 0
endfunction

private function Actions takes nothing returns boolean
    local integer orderId = table[GetIssuedOrderId()]
   
    if orderId != 0 then
        set units[n] = GetTriggerUnit()
        set deactivateOrder[n] = orderId
        if n == 0 then
            call TimerStart(tick, 0.00, false, function Callback)
        endif
        set n = n + 1
    endif
    return false
endfunction

function DisableAutocast takes integer activateOrder, integer deactivateOrder returns nothing
    local trigger t
    if table == 0 then
        set t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_ORDER)
        call TriggerAddCondition(t, Filter(function Actions))
        set table = Table.create()
    endif
    set table[activateOrder] = deactivateOrder
endfunction

endlibrary
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
If anyone wants a Lua version of this:

Lua:
do

local delay
local orderOn
local orderOff
local ordered

function DisableAutocast(activateOrder, deactivateOrder)
   if not orderOn then
      local t = CreateTrigger()
      TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_ORDER)
      TriggerAddCondition(t, Filter(function()
         local orderId = orderOn[GetIssuedOrderId()]
         if orderId then
            local index = #ordered + 1
            ordered[index] = GetTriggerUnit()
            orderOff[index]= orderId
            if index == 1 then
               TimerStart(delay, 0.00, nil, function()
                  for i = 1, #ordered do
                     IssueImmediateOrderById(ordered[i], orderOff[i])
                     ordered[i] = nil
                     orderOff[i] = nil
                  end
               end)
            end
         end
      ))
      delay       = CreateTimer()
      ordered     = {}
      orderOn     = {}
      orderOff    = {}
   end
   orderOn[activateOrder] = deactivateOrder
end

end
 
Top