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

How to prevent orders from interrupting SetUnitAnimationIndex?

Status
Not open for further replies.

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,543
I'm trying to create a system that prevents a unit from interrupting it's own spell casts when issuing orders (with the exception of "stop"). So once a unit starts casting a spell it can't Attack, Move, Patrol, or cast another spell until the current spell's animation is finished playing. The animations are played using SetUnitAnimationIndex and I use a Timer to ensure that the full animation plays. The issue is that the player can interrupt the Animation by issuing an order such as "Move". I'm looking for a way to get around this without straying too far from the normal Warcraft 3 mechanics. Also, remember that "Stop" should remain enabled as this is used to cancel the ability cast.

In other words, i'm looking for something like Cast Time (the Object Editor field) except without it's specific Animation limitations. That's the only thing stopping me from using it.

Things I tried:
1) Cast Stun on the unit. This almost works, as it prevents the unit from interrupting the animation with orders. However, it also prevents the use of the "Stop" order so it's a no go.

2) Disable the Move ("Amov") ability. This for whatever reason disables Move, Patrol, Stop, and Hold Position. So again I can't use this as I need "Stop" to remain enabled.

One idea that comes to mind is to use an ability based on Berserk. This ability would mimic Stop while casting a spell (It would have the "S" hotkey). So I would disable Move, which also disables Stop, then add the Berserk ability, and finally have a trigger to detect if the caster uses Berserk. If Berserk is used while casting then I would treat it as a "Stop" order and interrupt the cast.

Edit: I managed to get it to work with the Berserk ability idea, but it's still not ideal as disabling Move prevents you from issuing orders and thus prevents queuing up orders for your unit to do after the spell is completed.

Ideally, it would work like stun and allow you to queue orders while stunned so once the stun wears off or in this case once the ability finishes casting, the unit will immediately issue the most recent order it had been given.
 
Last edited:
Level 9
Joined
Sep 20, 2015
Messages
385
Have you tried to add a 0.01 wait before function animation is called?

  • Cast Anim
    • Events
      • Unit - A unit Begins casting an ability
    • Conditions
    • Actions
      • Wait 0.01 seconds
      • Custom script: call SetUnitAnimationByIndex( MyUnit, 0)
Also if the ability is based on channel it wont be interrupted i think
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,543
I don't have any issue with getting the unit to play the animation. I just want it so the unit plays the full animation without being interrupted by orders (with the exception of "Stop").

I use a 0.00 second timer to get the Animation to play rather than a 0.01 second wait.

Edit:
Channel's Follow Through Time is no good as it can be interrupted with any Order. And Disable Other Abilities = True is no good because it Pauses the unit and removes control completely.

I'm looking for something like Cast Time (The Object Editor field) but with the ability to play specific Animations.
 
Last edited:
Level 9
Joined
Sep 20, 2015
Messages
385
I think the ability type is important. I remember i had this problem and the 0.01 wait befreo the SetUnitAnimationIndex worked. But it was for shockwave and was like patch 1.26, so probably things ahve changed. What you can do at this point is to use cast time and edit the models animations
 
Last edited:
Level 10
Joined
Dec 11, 2009
Messages
234
1) Cast Stun on the unit. This almost works, as it prevents the unit from interrupting the animation with orders. However, it also prevents the use of the "Stop" order so it's a no go.
Have you tried using constant oskeytype OSKEY_S to catch the button press instead of catching ability used?
JASS:
native BlzTriggerRegisterPlayerKeyEvent            takes trigger whichTrigger, player whichPlayer, oskeytype key, integer metaKey, boolean keyDown returns event
constant native IsUnitSelected      takes unit whichUnit, player whichPlayer returns boolean
Obviously this won't apply for custom hotkey settings (or maybe there is a way?), but it should work, combined with stun approach.
As for command button, it still won't work, but you could do some custom-UI-voodoo magic to mimic that.
Wait, can you assign a hotkey for created button?
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,543
I didn't know that was a thing... I'll definitely do that if it works.

And i'm not sure if you can literally assign a hotkey to a button but you could always use this oskeytype method to run the same function that the button uses.

An issue that comes to mind. I have multiple units using this spell animation system and the map is intended to be played on battle.net. I'm worried that there would be a delay with getting the current subgroup units selected and cancelling their abilities when "S" is pressed.
 
Last edited:
Level 20
Joined
May 16, 2012
Messages
635
I think i know a way, but haven't test it yet. You can detect any order given to a unit using the Native Events:
JASS:
EVENT_PLAYER_UNIT_ISSUED_ORDER
,
JASS:
EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER
,
JASS:
EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER
.

The way to use it, is to check if the order being issued is the "Stop" order, which has the id 851972 and a variable that indicates that your unit is playing the animation. So if it is and its issued an order, you can pause and unpause the unit to prevent the animation being interrupted and if the order is "Stop" do nothing and the unit will stop playing the animation. Like this:

JASS:
scope AnimationTest initializer Init

private function Conditions takes nothing returns nothing
    local unit source = GetOrderedUnit() //Your unit
    local integer order = GetIssuedOrderId() // the order by id being issued

    //if not the Stop order (851972) and unit is doing waht you want then
    //pause and unpause to prevent the animation being canceled
    if order != 851972 and UnitIsPlayingAnimation then
        call PauseUnit(source, true)
        call PauseUnit(source, false)
    endif

    set source = null
endfunction
//===========================================================================
private function Init takes nothing returns nothing
    //RegisterPlayerUnitEvent() is a library to facilitate the creation of code
    // highly recommended: https://www.hiveworkshop.com/threads/snippet-registerplayerunitevent.203338/
    //obviously you could adjust it to use trigger variables and all.
    call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function Conditions)
    call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function Conditions)
    call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function Conditions)
endfunction

endscope

Also, it would be good to add in the condition the order id's of the spells you want to cast without interruption
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,543
@chopinski
That appears to work, thanks. One issue though, I printed a message whenever that trigger fires and it seems to fire like 100's of times per second. I think it's because when you Pause in response to an Order that Order is never cancelled and thus the trigger repeats constantly. Maybe I can add in a condition to prevent it from firing again. If I order the unit to "Stop" to interrupt the order then it breaks the Animation so I can't do that unfortunately.

Edit:
Alright, I managed to get it to work fairly well by checking if the unit is paused. It still fires the trigger like 20 times but it's not nearly as much as before:
Lua:
function CastIgnoreOrders()
    local trigger = CreateTrigger()
    TriggerRegisterAnyUnitEventBJ(trigger, EVENT_PLAYER_UNIT_ISSUED_ORDER)
    TriggerRegisterAnyUnitEventBJ(trigger, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
    TriggerRegisterAnyUnitEventBJ(trigger, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
    TriggerAddAction(trigger, function()
        local u = GetOrderedUnit()
        local o = GetIssuedOrderId()
        if o ~= 851972 and Info[u][IsCasting] and IsUnitPaused(u) == false then
            print("PAUSE")
            PauseUnit(u, true)
            PauseUnit(u, false)
        end
    end)
end
 
Last edited:
Level 20
Joined
May 16, 2012
Messages
635
@chopinski
That appears to work, thanks. One issue though, I printed a message whenever that trigger fires and it seems to fire like 100's of times per second. I think it's because when you Pause in response to an Order that Order is never cancelled and thus the trigger repeats constantly. Maybe I can add in a condition to prevent it from firing again. If I order the unit to "Stop" to interrupt the order then it breaks the Animation so I can't do that unfortunately.

Edit:
Alright, I managed to get it to work fairly well by checking if the unit is paused. It still fires the trigger like 20 times but it's not nearly as much as before:
Lua:
function CastIgnoreOrders()
    local trigger = CreateTrigger()
    TriggerRegisterAnyUnitEventBJ(trigger, EVENT_PLAYER_UNIT_ISSUED_ORDER)
    TriggerRegisterAnyUnitEventBJ(trigger, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
    TriggerRegisterAnyUnitEventBJ(trigger, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
    TriggerAddAction(trigger, function()
        local u = GetOrderedUnit()
        local o = GetIssuedOrderId()
        if o ~= 851972 and Info[u][IsCasting] and IsUnitPaused(u) == false then
            print("PAUSE")
            PauseUnit(u, true)
            PauseUnit(u, false)
        end
    end)
end


You sure no other unit in your map is being issued orders? because, this trigger will catch everything. Try printing the name of the unit to make sure. As far as i know the Pause() function do not issue any order to the unit, so it shouldnt be a problem.
 
Status
Not open for further replies.
Top