--STOLED FROM ARC_ LUL --[[ Anim queue: list of animation specifications that have to complete their animation one after the other Anim: an element (GUI, player, vehicle...) and a list of phases Anim phase: a list of parameters with start/end value, time, eventually a transformation function, and a function that applies the parameter to the element. May also have an attribute setting number of repeats (0 = infinite). --]] Animation = {} Animation.__index = Animation Animation.collection = {} function Animation.create(elem, ...) local anim = setmetatable({ type = 'anim', elem = elem, phases = {...} }, Animation) for i,phase in ipairs(anim.phases) do if type(phase) == 'table' then phase.from = phase.from or 0 phase.to = phase.to or 1 phase.time = phase.time or 0 phase.value = phase.from phase.speed = phase.time > 0 and ((phase.to - phase.from) / phase.time) or 0 end end return anim end function Animation.createAndPlay(elem, ...) local anim = Animation.create(elem, ...) anim:play() return anim end function Animation.createQueue(...) local queue = setmetatable({ type = 'queue' }, Animation) local args = { ... } if type(args[1]) == 'string' then queue.name = table.remove(args, 1) end for i,obj in ipairs(args) do queue:add(obj) end return queue end function Animation.createQueueAndPlay(...) local queue = Animation.createQueue(...) queue:play() return queue end function Animation.getQueue(name) for i,obj in ipairs(Animation.collection) do if obj:isQueue() and obj.name == name then return obj end end return false end function Animation.getOrCreateQueue(name) local queue = Animation.getQueue(name) if not queue then queue = Animation.createQueue(name) end return queue end function Animation:isPlaying() return self.playing or false end function Animation.playingAnimationsExist() return table.find(Animation.collection, 'playing', true) and true end function Animation:add(anim) if self:isQueue() then if type(anim) == 'function' then anim = setmetatable({ type = 'function', fn = anim }, Animation) end anim.queue = self table.insert(self, anim) end end function Animation:remove() if self.queue then table.removevalue(self.queue, self) if #self.queue == 0 then self.queue:remove() end else table.removevalue(Animation.collection, self) if not Animation.playingAnimationsExist() then removeEventHandler('onClientRender', root, updateAnim) Animation.prevTick = nil end end self.playing = false end function Animation:isAnimation() return self.type == 'anim' end function Animation:isQueue() return self.type == 'queue' end function Animation:play() if self:isPlaying() then return end if not table.find(Animation.collection, self) then table.insert(Animation.collection, self) end if not Animation.playingAnimationsExist() then Animation.prevTick = getTickCount() addEventHandler('onClientRender', root, updateAnim) end self.playing = true end function Animation:pause() self.playing = false if not Animation.playingAnimationsExist() then removeEventHandler('onClientRender', root, updateAnim) Animation.prevTick = nil end end function updateAnim() local phase local curTick = getTickCount() for i,obj in ipairs(Animation.collection) do if not isElement(obj.elem) then obj:remove() end if obj.playing then if obj:isQueue() then obj = obj[1] end phase = obj.phases[1] if (type(phase) == 'function') or (phase.speed > 0 and phase.value >= phase.to) or (phase.speed < 0 and phase.value <= phase.to) or (phase.speed == 0) then local doRemove = true if type(phase) == 'function' then phase(obj.elem) elseif phase.repeats then if phase.repeats == 0 then doRemove = false else phase.repeats = phase.repeats - 1 doRemove = phase.repeats == 0 end phase.starttick = getTickCount() phase.value = phase.from end if doRemove then table.remove(obj.phases, 1) phase = false if #obj.phases == 0 then obj:remove() obj = false end end end if obj and phase then if phase.fn then phase.fn(obj.elem, phase.transform and phase.transform(phase.value) or phase.value, phase) end if not phase.starttick then phase.starttick = curTick end phase.value = phase.from + phase.speed*(curTick - phase.starttick) end end end end Animation.presets = {} function Animation.presets.pulse(elem, value, phase) if not value then return { from = 0, to = 2*math.pi, transform = math.sin, time = elem, repeats = 0, fn = Animation.presets.pulse } else if not phase.width then phase.width, phase.height = guiGetSize(elem, false) phase.centerX, phase.centerY = guiGetPosition(elem, false) phase.centerX = phase.centerX + math.floor(phase.width/2) phase.centerY = phase.centerY + math.floor(phase.height/2) end local pct = 1 - (value+1)*0.1 local width = pct*phase.width local height = pct*phase.height local x = phase.centerX - math.floor(width/2) local y = phase.centerY - math.floor(height/2) guiSetPosition(elem, x, y, false) guiSetSize(elem, width, height, false) end end function table.removevalue(t, val) for i,v in ipairs(t) do if v == val then table.remove(t, i) return i end end return false end function table.find(tableToSearch, index, value) if not value then value = index index = false elseif value == '[nil]' then value = nil end for k,v in pairs(tableToSearch) do if index then if v[index] == value then return k end elseif v == value then return k end end return false end