• 🏆 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] Need some help with Delayed dummy ability

Status
Not open for further replies.
Level 15
Joined
Nov 30, 2007
Messages
1,202
I need some help with finishing this thing. I'm trying to cast a dummy ability over another one and wish to execute a callback once the dummy ability was fired.

But it also needs to detect cancel events such as Escape pressed and mouse click. The problem I'm having is that when I'm using the ability it doesn't always have time to execute as a cancel event can fire before that.

Basically what I want to happen is this. 1) Roar is cast 2) Give the unit a target ability and after a short delay force the player ot use it 3) Once the dummy ability has been used execute a callback 4) If escape or cancel was triggered but spell was not cast then remove the dummy ability.

So in essence there needs to be a check that is performed after a mosue click to see if it was followed up by an issue order event or something like that?

This is what I have so far
JASS:
library ForceAbilityLib uses Table optional TimerUtils
    globals
        private constant real MIN_DELAY                = 0.02    // This is the minimum acceptable delay
        private Table table                         = 0        // Cache
        private integer count                         = 0        // The number of active instances
    endglobals
    private function cleanup takes integer pid returns nothing
        call TriggerClearActions(table.trigger[pid])
        call DestroyTrigger(table.trigger[pid])
        call TriggerClearActions(table.trigger[pid - 25])
        call TriggerClearActions(table.trigger[pid - 50])
        call DestroyTrigger(table.trigger[pid])
        call DestroyTrigger(table.trigger[pid - 25])
        call DestroyTrigger(table.trigger[pid - 50])
        call table.trigger.remove(pid)
        call table.trigger.remove(pid - 25)
        call table.trigger.remove(pid - 50)
        call table.integer.remove(pid)
        call table.unit.remove(pid)
        set count = count - 1
        if count == 0 then
            call table.destroy()
            set table = 0
        endif
    endfunction
 
    /*
        This is not always cast
    */
    private function OnAbilityStart takes nothing returns nothing
        local unit u = GetTriggerUnit()
        local integer pid = GetPlayerId(GetOwningPlayer(u))
        if GetSpellAbilityId() == table.integer[pid] then
            call UnitRemoveAbility(u, table.integer[pid])
            call PauseUnit(u, true)
            call PauseUnit(u, false)
            call TriggerExecute(table.trigger[pid])
            call cleanup(pid)
        endif
        set u = null
    endfunction
 
    private function DelayedCancel takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local integer tid = GetHandleId(t)
        local integer pid = table.integer[tid]
        local unit u = table.unit[pid]
        call table.integer.remove(tid)
        if u != null then
            call UnitRemoveAbility(u, table.integer[pid])
            call PauseUnit(u, true)
            call PauseUnit(u, false)
            call cleanup(pid)
            set u = null
        endif
    endfunction
 
    /*
        Starts a delay before continuing with the cancel order, to allow the spell to be cast first.
    */
    private function OnCancel takes nothing returns nothing
        local timer t = CreateTimer()
        local integer tid = GetHandleId(t)
        set table.integer[tid] = GetPlayerId(GetTriggerPlayer())
        call TimerStart(t, 0.02, false, function DelayedCancel)
        set t = null
    endfunction
 
    /*
        Forces the order on the player associated with he expired timer.
    */
    private function DelayedForceAction takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local integer tid = GetHandleId(t)
        local integer pid = table.integer[tid]
        call ForceUIKeyBJ(table.player[tid], table.string[tid])
        call table.player.remove(tid)
        call table.string.remove(tid)
        call table.integer.remove(tid)
        call PauseTimer(t)
        call DestroyTimer(t)
        set t = null
    endfunction
 
    /*
        This will add an ability to the provided unit and force the owning player to use it after a small delay.
        When the dummy ability is cast a callback is triggered. After the ability has been cast or if the order
        was canceled the ability is removed.
    
        @Precondition: Unit must be selected by owning player.
    */
    function ForceAbility takes unit u, integer abilCode, string hotkey, real delay, code callback returns nothing
        local timer t
        local integer tid
        local player p
        local integer pid
        local trigger t1
        local trigger t2
        if delay < MIN_DELAY then  
debug        call BJDebugMsg("[AbilityOverride] ERROR: " + "Invalid delay argument: " + R2S(delay))
            return
        endif
        if table == 0 then
            set table = Table.create()
        endif
        set p = GetOwningPlayer(u)
        set pid = GetPlayerId(p)
        if table.integer.has(pid) then
debug        call BJDebugMsg("[AbilityOverride] ERROR: " + GetPlayerName(Player(pid)) + " already has a pending ability command.")
            set p = null
            return
        endif
        // Cancel
        set t1 = CreateTrigger()
        set table.trigger[pid - 25] = t1
        call TriggerAddAction(t1, function OnCancel)
        call TriggerRegisterPlayerEventEndCinematic(t1, p)
        call TriggerRegisterPlayerMouseEventBJ(t1, p, bj_MOUSEEVENTTYPE_DOWN)
        set t1 = null
        // Spell cast
        set t2 = CreateTrigger()
        set table.trigger[pid - 50] = t2
        call TriggerRegisterUnitEvent(t2, u, EVENT_UNIT_SPELL_CAST)
        call TriggerAddAction(t2, function OnAbilityStart)
        set t2 = null
        //
        set count = count + 1
        set t = CreateTimer()
        set tid = GetHandleId(t)
        set table.string[tid] = hotkey
        set table.player[tid] = p
        set table.integer[tid] = pid
        set table.integer[pid] = abilCode
        set table.trigger[pid] = CreateTrigger()
        set table.unit[pid] = u
        call TriggerAddAction(table.trigger[pid], callback)
        call UnitAddAbility(u, abilCode)
        call TimerStart(t, delay, false, function DelayedForceAction)
        set t = null
        set p = null
    endfunction
endlibrary

This is the usage I want to acomplish:
JASS:
scope TestSpell initializer Init
 
 
    /*
        I'd like to be able to retrieve what was targeted or the coordinate that was targeted
    */
    private function OnComplete takes nothing returns nothing
        local real x
        local real y
        // alternatively the object that was targeted
    
        // Create Special effect at...
    endfunction
    private function OnCast takes nothing returns boolean
        if GetSpellAbilityId() == 'A00E' then
            call ForceAbility(GetTriggerUnit(), 'A00F', "T", 0.03, function OnComplete) // Starting point
        endif
        return false
    endfunction
 
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_CAST)
        call TriggerAddCondition(t, Condition(function OnCast))
        set t = null
    endfunction
endscope

This is continuation of this: Programatically changing mouse cursor?

And the purpose of this is to implement it in the Spell Menu System, if I can get it to work smoothly enough.
 
Last edited:
Status
Not open for further replies.
Top