Anim = Anim or {} Ids = Ids or Require("lib/utility/shared/ids.lua") Anim.Active = Anim.Active or {} Anim.isUpdateLoopRunning = Anim.isUpdateLoopRunning or false --- This will request the animation dictionary. --- @param animDict string --- @return boolean function Anim.RequestDict(animDict) if not animDict or type(animDict) ~= "string" then return false end if HasAnimDictLoaded(animDict) then return true end RequestAnimDict(animDict) local timeout = GetGameTimer() + 2000 while not HasAnimDictLoaded(animDict) and GetGameTimer() < timeout do Wait(50) end return HasAnimDictLoaded(animDict) end --- This will start the animation update loop. function Anim.Start() if Anim.isUpdateLoopRunning then return end Anim.isUpdateLoopRunning = true CreateThread(function() while Anim.isUpdateLoopRunning do local idsToProcess = {} for idKey, _ in pairs(Anim.Active) do table.insert(idsToProcess, idKey) end if #idsToProcess == 0 then Wait(750) else for _, id in ipairs(idsToProcess) do local animData = Anim.Active[id] if animData then local entity = animData.entity local onComplete = animData.onComplete if not DoesEntityExist(entity) then if onComplete then onComplete(false, "despawned") end Anim.Active[id] = nil elseif animData.status == "pending_task" then TaskPlayAnim(entity, animData.animDict, animData.animName, animData.blendIn, animData.blendOut, animData.duration, animData.flag, animData.playbackRate, false, false, false) animData.startTime = GetGameTimer() animData.animEndTime = animData.duration > 0 and (animData.startTime + animData.duration) or -1 animData.status = "playing" elseif animData.status == "playing" then local animationCompletedNaturally = false if animData.duration == -1 then if not IsEntityPlayingAnim(entity, animData.animDict, animData.animName, 3) and GetEntityAnimCurrentTime(entity, animData.animDict, animData.animName) > 0.8 then animationCompletedNaturally = true end elseif animData.animEndTime ~= -1 and GetGameTimer() >= animData.animEndTime then animationCompletedNaturally = true end if animationCompletedNaturally then if onComplete then onComplete(true, "completed") end Anim.Active[id] = nil end end end end Wait(100) end end end) end --- This will play an animation on the specified ped. --- @param id string | nil --- @param entity number --- @param animDict string --- @param animName string --- @param blendIn number | nil --- @param blendOut number | nil --- @param duration number | nil --- @param flag number | nil --- @param playbackRate number | nil --- @param onComplete function | nil --- @return string | nil function Anim.Play(id, entity, animDict, animName, blendIn, blendOut, duration, flag, playbackRate, onComplete) local newId = id or Ids.CreateUniqueId(Anim.Active) if Anim.Active[newId] then if onComplete then onComplete(false, "id_in_use") end return newId end if not entity or not DoesEntityExist(entity) or not IsEntityAPed(entity) then if onComplete then onComplete(false, "invalid_entity") end return nil end if not Anim.RequestDict(animDict) then if onComplete then onComplete(false, "dict_load_failed") end return nil end Anim.Active[newId] = { entity = entity, animDict = animDict, animName = animName, blendIn = blendIn or 8.0, blendOut = blendOut or -8.0, duration = duration or -1, flag = flag or 1, playbackRate = playbackRate or 0.0, onComplete = onComplete, status = "pending_task", startTime = 0, animEndTime = 0 } Anim.Start() return newId end function Anim.Stop(id) if not id or not Anim.Active or not Anim.Active[id] then return false end local animData = Anim.Active[id] if animData.entity and DoesEntityExist(animData.entity) and IsEntityAPed(animData.entity) then if animData.status == "playing" or animData.status == "pending_task" then StopAnimTask(animData.entity, animData.animDict, animData.animName, 1.0) end end if animData.onComplete then animData.onComplete(false, "stopped_by_id") end Anim.Active[id] = nil local anyLeft = Anim.Active and next(Anim.Active) ~= nil if not anyLeft then Anim.isUpdateLoopRunning = false end return true end return Anim