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

UnitTimedAnimation

Level 10
Joined
Sep 21, 2007
Messages
517
This is a simple library which plays customizable timed unit animations for a unit. It can also play timed unit animations after a specific amount of time.

Requirements:

TimerUtils by Vexorian
UnitAppearanceTracker by Anitarf
Table by Vexorian

API is inside the code documentation

JASS:
library TimedUnitAnimation initializer Init requires TimerUtils, UnitAppearanceTracker, Table

//******************************************************************************
//* Author: Diehard@Azeroth at hiveworkshop.com
//*
//* This library is used to play unit animations for a specific time at a particular speed (animation speed that is).
//* It also is able to play timed unit animations after a specific amount of time.
//* 
//* 
//******************************************************************************
//*
//* Configuration: No configurables, easy and simple to use library
//* 
//* Importing (libraries needed, just google them):
//*  TimerUtils by Vexorian
//*  Table by Vexorian
//*  UnitAppearanceTracker by Anitarf
//*  Also make sure you have the latest version of vJASS to be able to compile the scripts (version 0.A.2.B)         
//* 
//******************************************************************************
//* 
//* Functions API:
//* 
//*
//*   function PlayUnitTimedAnimation takes unit u, string animation, real speedPercent, real time, real animationTime returns nothing
//* 
//*   Plays a unit animation for the unit u at a specific animation speed for a specific amount of time
//*   after a particular amount of time ;p In other words it plays a timed unit animation after an amount of time.
//*
//*   PARAMETERS:
//*
//*   u: the unit which will play the timed animation    
//*   animation: animation to be played by the unit  
//*   speedPercent: animation speed  for the animation played, it is in %, for ex 100% speed is the normal animation speed of a unit
//*   time: the amount of time to wait before playing the animation
//*   animationTime: the time in which the animation is to be played for. It is specified in seconds  
//*
//*
//*   function PlayUnitTimedAnimationByIndex takes unit u, integer animationIndex, real speedPercent, real time, real animationTime returns nothing
//* 
//*   Plays a timed unit animation after a specific amount of time. The animation is to be played based on the animation index.
//*
//*   PARAMETERS:
//*
//*   u: the unit which will play the timed animation    
//*   animationIndex is the animation index of the animation to be played for the unit. You can find this out in wc3 model viewer  
//*   speedPercent: animation speed  for the animation played, it is in %, for ex 100% speed is the normal animation speed of a unit
//*   time: the amount of time to wait before playing the animation
//*   animationTime: the time in which the animation is to be played for. It is specified in seconds  
//*
//*   function PlayLateUnitTimedAnimationWithRarity takes unit u, string animation, raritycontrol rarity, real speedPercent, real animationTime returns nothing
//* 
//*   Plays a timed unit animation after a specific amount of time.
//*   The played animation will differ based on rarity control if it is not a specific animation; this is
//*   the exact same concept and implementation as the original wc3 function.
//*
//*   PARAMETERS:
//*
//*   u: the unit which will play the timed animation    
//*   animation: animation to be played by the unit
//*   rarity: when dealing with random animations, it will allow you to either play the rare or common animation.
//*   speedPercent: animation speed  for the animation played, it is in %, for ex 100% speed is the normal animation speed of a unit
//*   time: the amount of time to wait before playing the animation
//*   animationTime: the time in which the animation is to be played for. It is specified in seconds 
//*
//*    HINT: To play an immediate timed animation, just set the time parameter to 0
//*
//******************************************************************************
//* Credits: 
//* Thanks to Purgeandfire111 and Deaod for tips regarding efficiency and/or debugging
//* A huge thanks to Anitarf for providing great advice on improving this library
//* Please give credits if you use this library
//******************************************************************************
/*
        IMPORTANT NOTICE: This is for any possible animation overlapping done by the user
        
        *The order in which animations play on the same unit is based on the time in which they are played.
        *The animation speed which the unit reverts to is the animation speed before the unit starts playing
        any set of overlapping animations
        
        Lets say an animation A is to be played in 6 seconds.
        Lets say an animation B is to be played in 4 seconds.
        First Animation A is to be played and then animation B is to be played
        After 4 seconds animation B is played. After another two seconds animation A overrides animation B !
        
        When i say animation A overrides animation B, i mean the unit plays animation A with the animation speed
        you specified. 
        
        The animation speed the unit will revert to is the animation speed before the unit played its first animation.

*/

    private struct anim_data
        unit animatedUnit
        string animation
        integer animationIndex
        real animationSpeed
        real previousAnimationSpeed
        real animationTime
        integer animationPlayMode
        raritycontrol rarity
    endstruct
    
    globals
        private HandleTable unitPlayAnimation
    endglobals
    

    
    private function AnimationEnd takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local anim_data dat = GetTimerData(t)
        
        // check if the amount of unit animations queued to the unit are none, if so then reset the animation speed and animation
        // else just wait for the queued animations to finish
        set unitPlayAnimation[dat.animatedUnit] = unitPlayAnimation[dat.animatedUnit] - 1
        
        if unitPlayAnimation[dat.animatedUnit] == 0 then
            call SetUnitTimeScale(dat.animatedUnit, dat.previousAnimationSpeed) 
            call SetUnitAnimation(dat.animatedUnit, "stand")
        endif
        
        call ReleaseTimer(t)
        call dat.destroy()
        
        set t = null
    endfunction
    
    
    
    //---------------------------------------------------------------------------------------------------------------------
    
    private function PlayLateAnimation takes nothing returns nothing
        local timer expiredTimer = GetExpiredTimer()
        local anim_data dat = GetTimerData(expiredTimer)
        local timer animationTimer = NewTimer()
        
        // increment the amount of animations queued to the unit
        // whichever animation comes first will play first, 
        set unitPlayAnimation[dat.animatedUnit] = unitPlayAnimation[dat.animatedUnit] + 1
        
        // play animation with a specific speed
        if dat.animationPlayMode == 0 then
            call SetUnitAnimation(dat.animatedUnit, dat.animation)
        elseif dat.animationPlayMode == 1 then
            call SetUnitAnimationByIndex(dat.animatedUnit, dat.animationIndex)
        elseif dat.animationPlayMode == 2 then
            call SetUnitAnimationWithRarity(dat.animatedUnit, dat.animation, dat.rarity)
        else
            // if no animation is specified, play the default animation
            call SetUnitAnimation(dat.animatedUnit, "stand")
        endif
        
        call SetUnitTimeScale(dat.animatedUnit, dat.animationSpeed*0.01)
        
        // set off a timer and recycle the expired timer
        call SetTimerData(animationTimer, dat)
        call TimerStart(animationTimer, dat.animationTime, false, function AnimationEnd)
        
        
        call ReleaseTimer(expiredTimer)
        set expiredTimer = null
        set animationTimer = null
    endfunction
    
    function PlayUnitTimedAnimation takes unit u, string animation, real speedPercent, real time, real animationTime returns nothing
        local timer t = NewTimer()
        local anim_data dat = anim_data.create()
        
        set dat.animatedUnit = u
        // only set the previous animation speed as the animation speed before any queue of animations
        if unitPlayAnimation[dat.animatedUnit] == 0 then
            set dat.previousAnimationSpeed = GetUnitTimeScale(u)
        endif
        set dat.animation = animation
        set dat.animationSpeed = speedPercent
        set dat.animationTime = animationTime
        set dat.animationPlayMode = 0
        
        call SetTimerData(t, dat)
        call TimerStart(t, time, false, function PlayLateAnimation)

        set t = null
    endfunction
    
    function PlayUnitTimedAnimationByIndex takes unit u, integer animationIndex, real speedPercent, real time, real animationTime returns nothing
        local timer t = NewTimer()
        local anim_data dat = anim_data.create()
        
        set dat.animatedUnit = u
        if unitPlayAnimation[dat.animatedUnit] == 0 then
            set dat.previousAnimationSpeed = GetUnitTimeScale(u)
        endif
        set dat.animationIndex = animationIndex
        set dat.animationSpeed = speedPercent
        set dat.animationTime = animationTime
        set dat.animationPlayMode = 1
        
        call SetTimerData(t, dat)
        call TimerStart(t, time, false, function PlayLateAnimation)

        set t = null
    endfunction
    
    
    function PlayUnitTimedAnimationWithRarity takes unit u, string animation, raritycontrol rarity, real speedPercent, real time, real animationTime returns nothing
        local timer t = NewTimer()
        local anim_data dat = anim_data.create()
        
        set dat.animatedUnit = u
        if unitPlayAnimation[dat.animatedUnit] == 0 then
            set dat.previousAnimationSpeed = GetUnitTimeScale(u)
        endif
        set dat.animation = animation
        set dat.rarity = rarity
        set dat.animationSpeed = speedPercent
        set dat.animationTime = animationTime
        set dat.animationPlayMode = 2
        
        call SetTimerData(t, dat)
        call TimerStart(t, time, false, function PlayLateAnimation)

        set t = null
    endfunction
    
    
    //------------------------------------------------------------------
    private function Init takes nothing returns nothing
        set unitPlayAnimation = Table.create()
    endfunction
    
endlibrary


UPDATED: Used keys instead of integers and TimerUtils instead of destroying timers
UPDATE 2: Code optimized, simplified API and made sure the animation speed at which a unit first begins an animation after will have the same animation speed reverted to the unit after the unit finished playing the animation (just read the doc. lol)
 
Last edited:
Level 10
Joined
Sep 21, 2007
Messages
517
Haha damn, guess im still living in the past. I tried using TimerUtils but i remember getting a problem with the static ifs and DEBUG_MODE constant. Its wierd i have latest version of jass helper and JNGP. I will check and see if it works. I will look up the key variable type. Thanks Deaod

*edit: wow Keys are pretty useful, vJASS is amazing

@Deaod, i will consider making changes if a mod checks this resource out, else its a waste of time. Besides i need to make sure my JNGP/JASSHelper become compatible with what i said above :]
 
Last edited by a moderator:
Level 10
Joined
Sep 21, 2007
Messages
517
@Bribe, Excuse me about the double post dude, didn't know people cared that much.

Anyways bro, i didnt mean anything about the resource not being checked yet, no hard feelings. It's amazing your doing all of this work alone, keep it up! Our jass is pretty much up-to-par in terms of efficiency, but i really think some scripts are somewhat useless and are loaded with unneeded modules and whatnot compared to, for example, wc3campaigns.net. Not that i think we will have someone as good as vexorian here, or anywhere for that matter.

I believe i have jasshelper 0.a.2.b, i will try re-updating it again. Vexorian's systems really made my life easier before... and still will ;p Peace
 
You should use structs and then the SetTimerData utility of TimerUtils. That way, you are just using global arrays + saving 1 integer rather than saving a bunch of handles. Ex:

JASS:
private struct Sample
    private unit caster
    private real time

    private static method periodic takes nothing returns nothing
        local thistype this = GetTimerData(GetExpiredTimer())
        set this.time = this.time + 34
        if this.time > 52 then
            call ReleaseTimer(GetExpiredTimer())
        endif 
    endmethod

    static method sampleMethod takes unit u, real s returns nothing
        local thistype this = thistype.allocate()
        local timer t = NewTimer()
        set this.caster = u
        set this.time = s
        call SetTimerData(t, this)
        call TimerStart(t, 0.03125, true, function thistype.periodic)
    endmethod
endstruct

That is just a sample of using the timer. You can still use regular functions and then call a method within the struct, or you can use regular functions and then handle the struct stuff within that function. It is really up to you how to go about it.

I suppose it isn't completely necessary, but it is a bit more efficient and you might as well do it if you are gonna use TimerUtils.
 
Last edited:
Level 10
Joined
Sep 21, 2007
Messages
517
Wow, TimerUtils can save structs? How is a struct considered an integer? From C++, i know that a struct is like a class except it only has public members and is mainly used to manipulate data. Anyways isn't a struct an object? And if so how can it be treated as an integer?

Thanks for the elaborate example, +rep
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
You have to remember that vJass is nothing but jass with some candy eye stuff, it's only a jass preprocessor, in the end it's still jass, there is no more jass stuff added.
So yes vJass structs are just basically a bunch of global arrays.
And a struct instance is an integer : the index of these arrays.

Check the file "outputwar3map.j" in the subfolder "logs" of your JNGP once you've saved the script.

However if the typecast structinstance -> integer is implicit, you can make it explicit : integer(<structinstance>).
And you always have to make explicit the typecast integer -> structinstance : structname(<integer>) or thistype(<integer>) if it's within the struct, else you will have an error on code parsing.
 
Top