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

Advanced Ability Animations

This bundle is marked as pending. It has not been reviewed by a staff member yet.
A simple system that enables you to give any ability a unique Art - Animation - Cast Point as well as any Animation of your choosing.

Cast Point is the value that determines how long it takes to cast a spell. Normally, you define this value once on your Unit and it applies to ALL of their abilities. With this system, you can bypass that limitation and assign this value on a per ability basis. You can also choose to play a specific Animation by relying on the SetUnitAnimationByIndex() function which uses Integers instead of Strings to give you exact control. This is useful since the Art - Animation Names field that Abilities use is limited in what it can play. Furthermore, the system will adjust your casting Unit's timescale (Animation speed) in order to play the entire Animation within the given time frame.

However, do note that it comes with some requirements:
1) Your Unit needs to have it's Art - Animation - Cast Point set to 0.00 for it to work smoothly.
2) Your Ability needs to have it's Casting Time set to it's new Cast Point value. This may not work with every single ability.
3) Your Ability needs to have it's Art - Animation Names set to stand for it to work smoothly.

(Technically you could tweak #1 and #3 and it'd still work, just with different results than what I was going for here)

This system is GUI friendly but you could easily use it in Jass. It's designed to use GUI to register new abilities to the system:
  • AAA Demo
    • Events
      • Time - Elapsed game time is 0.00 seconds
    • Conditions
    • Actions
      • -------- How to use: --------
      • -------- --------
      • -------- 1) Set your casting unit's Art - Animation - Cast Point to 0.00. --------
      • -------- 2) Set your abilities Casting Time to whatever you want it's new Cast Point to be. This is how long it takes to cast! --------
      • -------- 3) Set your abilities Art - Animation Names to stand. --------
      • -------- 4) Set the four AAA__ variables below. --------
      • -------- - Abil_Type is the ability that you want to register to the system. --------
      • -------- - Anim_Index is the Integer id of the animation you want to play. You can find this value in a Model Editor or on Hiveworkshop's 3D Model Viewer. --------
      • -------- - Anim_Duration is the duration in seconds of your animation. (You can tweak this to make the animation play faster/slower) --------
      • -------- - Anim_Cast_Time is the duration in seconds it takes to cast the ability. This should match the Casting Time of your ability in the Object Editor! --------
      • -------- 5) Call the AAA_Register_Ability() function to add this ability to the system. --------
      • -------- --------
      • -------- In this example I am changing Holy Light to use the "stand victory" animation and take 1.0 second to cast. --------
      • Set VariableSet AAA__Abil_Type = Holy Light
      • Set VariableSet AAA__Anim_Index = 7
      • Set VariableSet AAA__Anim_Duration = 2.66
      • Set VariableSet AAA__Cast_Time = 1.00
      • Custom script: call AAA_Register_Ability()
Notes:
It can be difficult to find the correct Animation Index since Animations aren't ordered in a logical way in the World Editor.
What I do is go into the Hiveworkshop Model section and find a similar model that uses the same set of Animations.
Then click the View In 3D button to view the model. You should see all of it's Animations listed out in the proper order on the right side.
If it's still playing the wrong animation then try subtracting 1 from Anim_Index or adding 1 to it. It can be inconsistent.

Also, if all of the Units using this system have their Art - Animation - Cast Backswing set to 0.00 then you can set this variable to false to improve performance:
vJASS:
    globals
        private constant boolean DOES_USE_CAST_BACKSWING = true

        // Define the above DOES_USE_CAST_BACKSWING boolean.
        // If any unit has an Art - Animation - Cast Backswing > 0.00 then
        // keep this set to true. Otherwise, set it to false to make the
        // system slightly more efficient.


vJASS:
library AdvancedAbilityAnimations initializer Init
    // Created by Uncle

    globals
        private constant boolean DOES_USE_CAST_BACKSWING = true

        // Define the above DOES_USE_CAST_BACKSWING boolean.
        // If any unit has an Art - Animation - Cast Backswing > 0.00 then
        // keep this set to true. Otherwise, set it to false to make the
        // system slightly more efficient.


        // Do not modify anything below this line
        private hashtable hash = InitHashtable()
        private unit caster
        private integer abilityId
        private integer timerHandle
        private timer abilityTimer

        // References to your AAA_ variables
        private integer castAnim // animation index
        private real castDuration // animation standard duration
        private real castTime // animation desired duration

        // KEYS in the Hashtable
        private constant integer KEY_UNIT = 0
        private constant integer KEY_ABIL = 1
        private constant integer KEY_ANIM = 0
        private constant integer KEY_DURATION = 1
        private constant integer KEY_TIME = 2
    endglobals

    private function StopCasting takes nothing returns nothing
        set abilityId = GetSpellAbilityId()
        set castTime = LoadReal(hash, abilityId, KEY_TIME)
        if castTime == 0.0 then
            return
        endif

        set caster = GetTriggerUnit()
        call SetUnitTimeScale(caster, 1.0)
        call ResetUnitAnimation(caster)
 
        set abilityTimer = LoadTimerHandle(hash, GetHandleId(caster), 0)
        if abilityTimer != null then
            call FlushChildHashtable(hash, GetHandleId(abilityTimer))
            call FlushChildHashtable(hash, GetHandleId(caster))
            call DestroyTimer(abilityTimer)
        endif
    endfunction
 
    private function BeginCastingCallback takes nothing returns nothing
        set abilityTimer = GetExpiredTimer()
        set timerHandle = GetHandleId(abilityTimer)

        set caster = LoadUnitHandle(hash, timerHandle, KEY_UNIT)
        if IsUnitType(caster, UNIT_TYPE_DEAD) or GetUnitTypeId(caster) == 0 then
            return
        endif
        set abilityId = LoadInteger(hash, timerHandle, KEY_ABIL)
 
        // Variables used to play the special cast animation
        set castAnim = LoadInteger(hash, abilityId, KEY_ANIM)
        set castDuration = LoadReal(hash, abilityId, KEY_DURATION)
        set castTime = LoadReal(hash, abilityId, KEY_TIME)

        call SetUnitAnimationByIndex(caster, castAnim)
        call SetUnitTimeScale(caster, castDuration / castTime)

        call FlushChildHashtable(hash, timerHandle)
        call FlushChildHashtable(hash, GetHandleId(caster))
        call DestroyTimer(abilityTimer)
    endfunction

    private function BeginCasting takes nothing returns nothing
        set abilityId = GetSpellAbilityId()
        set castTime = LoadReal(hash, abilityId, KEY_TIME)
        if castTime == 0.0 then
            return
        endif

        set caster = GetTriggerUnit()
        set abilityTimer = CreateTimer()
        set timerHandle = GetHandleId(abilityTimer)

        call SaveUnitHandle(hash, timerHandle, KEY_UNIT, caster)
        call SaveInteger(hash, timerHandle, KEY_ABIL, abilityId)
        call SaveTimerHandle(hash, GetHandleId(caster), 0, abilityTimer)

        call TimerStart(abilityTimer, 0, false, function BeginCastingCallback)
    endfunction

    // Call this after setting up the AAA_ variables
    function AAA_Register_Ability takes nothing returns nothing
        local integer id = udg_AAA__Abil_Type
        if udg_AAA__Anim_Duration <= 0 or udg_AAA__Cast_Time <= 0 then
            call DisplayTextToPlayer(Player(0), 0, 0, "[AAA Error] Ability " + GetAbilityName(id) + " was not registered! Anim Duration and Cast Time must be > 0.")
            return
        endif
        call SaveInteger(hash, id, KEY_ANIM, udg_AAA__Anim_Index)
        call SaveReal(hash, id, KEY_DURATION, udg_AAA__Anim_Duration)
        call SaveReal(hash, id, KEY_TIME, udg_AAA__Cast_Time)
    endfunction
 
    private function Init takes nothing returns nothing
        local trigger t1 = CreateTrigger()
        local trigger t2 = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ( t1, EVENT_PLAYER_UNIT_SPELL_CHANNEL )
        call TriggerRegisterAnyUnitEventBJ( t2, EVENT_PLAYER_UNIT_SPELL_ENDCAST )

        // Art - Cast Backswing may need to be accounted for (if > 0.00)
        if DOES_USE_CAST_BACKSWING then
            call TriggerRegisterAnyUnitEventBJ( t2, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        endif

        call TriggerAddAction( t1, function BeginCasting )
        call TriggerAddAction( t2, function StopCasting )
        set t1 = null
        set t2 = null
    endfunction
 
endlibrary
v1.0: Released.
v1.1: Added DOES_USE_CAST_BACKSWING boolean to improve performance when Art - Animation - Cast Backswing is set to 0.00 for your Units that use this system, added more If condition checks to prevent unnecessary code from running, fixed unit handle memory leak.
v1.2: Adjusted code syntax and added an error message for when an ability fails to register. Renamed map to have spaces in title.
Previews
Contents

Advanced Ability Animations v.1.2 (Map)

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,468
This would probably make more sense as something that inserts into Spell System, to avoid having to configure two separate things for spells. If you want, I could add some //! runtextmacro optional statements inside of Spell System, like I did with Damage Engine, to allow your system to be optionally supported and integrated.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,543
This would probably make more sense as something that inserts into Spell System, to avoid having to configure two separate things for spells. If you want, I could add some //! runtextmacro optional statements inside of Spell System, like I did with Damage Engine, to allow your system to be optionally supported and integrated.
Sounds good to me, I don't really have much attachment to this, it's quite simple after all.
 
Top