Run it
Copy the script and paste it into your executor.
(msg) (() ( .. (msg)) )
plrs = :()
as = :()
aes = :()
rs = :()
hs = :()
cp = :()
uis = :()
mps = :()
lp = plrs.LocalPlayer
env = (() == ())
(env.UgcAnimStudioCleanup) == (env.UgcAnimStudioCleanup)
connections = {}
(c) connections[#connections + ] = c; c
toast
onMetaChanged
onHistoryChanged
camera = .CurrentCamera :(, )
vpSize = camera camera.ViewportSize .(, )
W0, H0 = ,
baseScale = .(.(vpSize.X / , vpSize.Y / ), , ) *
scale = .(baseScale, (vpSize.X * ) / W0, (vpSize.Y * ) / H0)
scale <= scale ~= scale scale =
(n) .(n * scale)
C = {
bg = .(, , ),
panel = .(, , ),
surface = .(, , ),
surfaceHi = .(, , ),
stroke = .(, , ),
accent = .(, , ),
accent2 = .(, , ),
text = .(, , ),
textDim = .(, , ),
textMuted = .(, , ),
overlay = .(, , ),
green = .(, , ),
red = .(, , ),
gold = .(, , ),
orange = .(, , ),
pillBg = .(, , ),
pillTx = .(, , ),
badBg = .(, , ),
badTx = .(, , ),
wearBg = .(, , ),
}
WHITE = .(, , )
(class, props, parent)
o = .(class)
k, v (props {}) o[k] = v
parent o.Parent = parent
o
EASE = {
Linear = (x) x ,
Quad = (x) - ( - x) * ( - x) ,
Quint = (x) y = - x; - y * y * y * y * y ,
Back = (x)
c1 =
y = x -
+ (c1 + ) * y * y * y + c1 * y * y
,
}
(a, b, e)
ty = (a)
ty == a + (b - a) * e
a:(b, e)
activeTweens, tweenSlots = {}, ({}, {__mode = })
tweenConn
TweenObj = {}
TweenObj.__index = TweenObj
TweenObj:() self.cancelled =
TweenObj.Destroy = TweenObj.Cancel
(rec)
slots = tweenSlots[rec.obj]
slots
prop (rec.p)
slots[prop] == rec slots[prop] =
()
now = .()
n = #activeTweens
i =
i <= n
rec = activeTweens[i]
o = rec.obj
done = rec.cancelled o.Parent == (rec.p) ==
done
x = (now - rec.t0) / rec.dur
rec.loop
x = x - .(x)
x >=
x =
done =
e = rec.(x)
prop, pr (rec.p)
done rec.loop
o[prop] = pr[]
o[prop] = (pr[], pr[], e)
done
(rec)
activeTweens[i] = activeTweens[n]
activeTweens[n] =
n -=
i +=
n == tweenConn
tweenConn:()
tweenConn =
(o, time, props, style, dir, loop)
slots = tweenSlots[o]
slots slots = {}; tweenSlots[o] = slots
rec = ({
obj = o, p = {}, t0 = .(), dur = .(time, ), loop = loop ,
ease = EASE[style style.Name ] EASE.Quad,
}, TweenObj)
dir == .EasingDirection.In rec.loop == style style.Name ~=
out = rec.ease
rec.ease = (x) - ( - x)
prop, goal (props)
old = slots[prop]
old old.p[prop] =
ok, from = (() o[prop] )
ok from ~=
rec.p[prop] = {from, goal}
slots[prop] = rec
(rec.p) == rec
activeTweens[#activeTweens + ] = rec
tweenConn tweenConn = rs.RenderStepped:(tweenStep)
rec
()
tweenConn tweenConn:(); tweenConn =
.(activeTweens)
(root)
list = {}
(obj)
isText = obj:() obj:() obj:()
list[#list + ] = {obj = obj, bgT = obj.BackgroundTransparency, txtT = isText obj.TextTransparency }
_, c (obj:())
c:() (c)
(root)
list
(p, r) (, {CornerRadius = .(, r ())}, p)
(p) (, {CornerRadius = .(, )}, p)
(p, color, th, tr)
(, {Color = color C.stroke, Thickness = th , Transparency = tr , ApplyStrokeMode = .ApplyStrokeMode.Border}, p)
(p, l, r, t, b)
(, {PaddingLeft = .(, l ), PaddingRight = .(, r ), PaddingTop = .(, t ), PaddingBottom = .(, b )}, p)
shimmers = {}
(obj, c1, c2, speed)
shimmers[obj]
grad = (, {
Name = , Rotation = , Offset = .(-, ),
Color = ColorSequence.({ColorSequenceKeypoint.(, c1), ColorSequenceKeypoint.(, c2), ColorSequenceKeypoint.(, c1)}),
}, obj)
tween = (grad, speed , {Offset = .(, )}, .EasingStyle.Linear, .EasingDirection.In, )
shimmers[obj] = {grad, tween}
(obj)
d = shimmers[obj]
d
(() d[]:() )
(() d[]:() )
shimmers[obj] =
btnState = ({}, {__mode = })
(b)
b.MouseEnter:(()
st = btnState[b]; st
st.hover =
(b, , {BackgroundColor3 = st.bg:(WHITE, )})
)
b.MouseLeave:(()
st = btnState[b]; st
st.hover =
(b, , {BackgroundColor3 = st.bg})
)
b.MouseButton1Down:(()
st = btnState[b]; st
(b, , {BackgroundColor3 = st.bg:(.(, , ), )})
)
b.MouseButton1Up:(()
st = btnState[b]; st
(b, , {BackgroundColor3 = st.hover st.bg:(WHITE, ) st.bg})
)
(parent, props, bg, bgT, txt)
props = props {}
props.AutoButtonColor =
props.BorderSizePixel =
props.Font = props.Font .Font.GothamBold
props.TextSize = props.TextSize ()
props.BackgroundColor3 = bg
props.BackgroundTransparency = bgT
props.TextColor3 = txt C.text
b = (, props, parent)
btnState[b] = {bg = bg, hover = }
(b)
b
(b, bg, bgT, txt)
st = btnState[b]; st
st.bg = bg
(b, , {
BackgroundColor3 = st.hover bg:(WHITE, ) bg,
BackgroundTransparency = bgT ,
TextColor3 = txt b.TextColor3,
})
(n)
str = (.((n) ))
out = str:():(, ):()
out:(, ) == out = out:()
out
(enumName, itemName)
ok, v = (() [enumName][itemName] )
ok (v) == v
(p, name, value) ((() p[name] = value ))
m = {
[] = , [] = , [] = , [] = ,
[] = , [] = , [] = , [] = ,
}
reverseM = {
[] = , [] = , [] = , [] = ,
[] = , [] = , [] = , [] = ,
}
shortNames = {
[] = , [] = , [] = , [] = ,
[] = , [] = , [] = , [] = ,
}
buttonOrder = {, , , , , , , }
CARD_ORDER = {, , , , , , }
DESC_FOLDERS = {
IdleAnimation = {}, WalkAnimation = {}, RunAnimation = {}, JumpAnimation = {},
FallAnimation = {}, ClimbAnimation = {}, SwimAnimation = {, },
}
ASSET_TYPE_IDS = {
[] = , [] = , [] = , [] = ,
[] = , [] = , [] = ,
}
CACHE_FILE_NAME =
ANIM_CACHE_URL =
EQUIPPED_FILE =
SAVED_BUNDLES_FILE =
HISTORY_FILE =
HISTORY_MAX =
PRESETS_FILE =
SETTINGS_FILE =
LAST_PACK_FILE =
(file)
ok, content = (()
isfile (file)
(file)
)
ok (content) == content ~=
suc, decoded = (() hs:(content) )
suc (decoded) == decoded
{}
(file, data)
(() (file, hs:(data)) )
queued = {}
(file, data)
queued[file]
queued[file] =
.(, ()
queued[file] =
(file, data)
)
assetCache, inflight, detailsCache, metaCache = {}, {}, {}, {}
fileCache = (CACHE_FILE_NAME)
equippedAnims = (EQUIPPED_FILE)
savedBookmarks = (SAVED_BUNDLES_FILE)
historyList = (HISTORY_FILE)
presetList = (PRESETS_FILE)
scriptSettings = (SETTINGS_FILE)
lastPackData = (LAST_PACK_FILE)
(presetList) ~= presetList[] == presetList = {}
(historyList) ~= historyList[] == historyList = {}
scriptSettings.globalSpeed scriptSettings.globalSpeed =
scriptSettings.customSpeeds scriptSettings.customSpeeds = {}
scriptSettings.advanceSpeed == scriptSettings.advanceSpeed =
scriptSettings.restoreLastPack == scriptSettings.restoreLastPack =
scriptSettings.forceAnimation == scriptSettings.forceAnimation =
() (SETTINGS_FILE, scriptSettings)
.(()
ok, body = (() :(ANIM_CACHE_URL) )
ok (body) ==
ok2, data = (() hs:(body) )
ok2 (data) ==
k, v (data) fileCache[k] fileCache[k] = v
(CACHE_FILE_NAME, fileCache)
)
(t) (.((t )):(, ))
(item)
n = (item.AssetType)
n ASSET_TYPE_IDS[n] ASSET_TYPE_IDS[n]
lt = (item.AssetType)
lt == shortNames[lt] lt
nm = .((item.Name ))
nm:()
nm:()
nm:()
nm:()
nm:()
nm:()
nm:()
(items)
map = {}
_, item (items {})
slot = (item)
slot
payload = {Id = item.Id, Name = item.Name, AssetType = slot}
slot ==
map.swimanimation = payload
map.swimidleanimation = payload
map[slot] = payload
map
(assetType)
lt = (assetType)
lt:() lt:()
lt:()
lt:()
lt:()
lt:()
lt:()
lt:()
(id, bundleId, assetType)
t, ser = {}, {}
descProp = (assetType)
(()
desc = .()
desc[descProp] = (id)
dummy = plrs:(desc, .HumanoidRigType.R15)
animate = dummy:()
animate
_, folderName (DESC_FOLDERS[descProp] {})
folder = animate:(folderName)
folder
_, child (folder:())
child:() child.AnimationId ~=
clone = child:()
t[#t + ] = clone
ser[#ser + ] = {Name = clone.Name, AnimationId = clone.AnimationId, BundleId = (bundleId)}
dummy:()
)
#t ==
(()
objs = :( .. (id))
objs #objs >
(item)
item:()
t[#t + ] = item:()
ser[#ser + ] = {Name = item.Name, AnimationId = item.AnimationId, BundleId = (bundleId)}
(objs[])
_, c (objs[]:()) (c)
)
t, ser
(id, bundleId, assetType)
sid = (id)
assetCache[sid] assetCache[sid]
cached = fileCache[sid]
(cached) == #cached >
rebuilt = {}
_, d (cached)
d.AnimationId d.AnimationId ~=
a = .()
a.Name = d.Name
a.AnimationId = d.AnimationId
rebuilt[#rebuilt + ] = a
#rebuilt > assetCache[sid] = rebuilt; rebuilt
inflight[sid]
t0 = .()
inflight[sid] .() - t0 < .()
assetCache[sid] {}
inflight[sid] =
ok, t, ser = (fetchAnimations, id, bundleId, assetType)
inflight[sid] =
ok (t) ~= {}
#t >
assetCache[sid] = t
fileCache[sid] = ser
(CACHE_FILE_NAME, fileCache)
t
(id)
sid = (id)
assetCache[sid], fileCache[sid] = ,
(CACHE_FILE_NAME, fileCache)
(fetchedTracks, targetType)
#fetchedTracks ==
targetType ==
_, tr (fetchedTracks)
.(tr.Name):() tr
fetchedTracks[] fetchedTracks[]
targetType ==
_, tr (fetchedTracks)
.(tr.Name) == tr
fetchedTracks[]
(payload, slot)
payload.Direct
payload.Tracks
out = {}
_, d (payload.Direct)
d.AnimationId d.AnimationId ~=
a = .()
a.Name = d.Name
a.AnimationId = d.AnimationId
out[#out + ] = a
payload.Tracks = out
payload.Tracks
list = (payload.Id, , payload.AssetType)
#list == {}
slot == list
spec = (list, slot)
spec {spec} {}
(list)
#list > (() cp:(list) )
(key)
e = savedBookmarks[key]
(e) == e.Items e
_, h (historyList)
(h.Id) == key h.Items h
(id)
key = (id)
detailsCache[key] detailsCache[key]
stored = (key)
stored
res = {Items = stored.Items}
detailsCache[key] = res
res
ok, res = (() as:(id) )
ok (res) == res.Items
detailsCache[key] = res
res
(value)
raw = (value )
digits = raw:()
digits raw
(slot)
scriptSettings.advanceSpeed slot
(scriptSettings.customSpeeds[slot])
(scriptSettings.globalSpeed)
(track, slot)
track
speed = (slot)
currentSpeed
(() currentSpeed = track.Speed )
(currentSpeed) ~= .(currentSpeed - speed) >
(() track:(speed) )
(tr)
animation = tr tr.Animation
animation
character = lp.Character
animate = character character:()
node = animation
node node ~= animate
slot = reverseM[.(node.Name)]
slot slot
node = node.Parent
targetId = (animation.AnimationId)
animate targetId ~=
folderName, slot (reverseM)
folder = animate:(folderName)
folder
_, item (folder:())
item:() (item.AnimationId) == targetId
slot
sName, list (equippedAnims)
_, anim (list)
anim.Name == animation.Name (anim.AnimationId) == targetId
sName
(tr)
animation = tr tr.Animation
animation
pName = animation.Parent .(animation.Parent.Name)
animationName = .(animation.Name )
isTool = pName == animationName:(, , ) ~=
scriptSettings.forceAnimation isTool
tr:()
(tr, (tr))
()
character = lp.Character
human = character character:()
animator = human human:()
animator
_, tr (animator:())
(tr)
hookedAnimators = ({}, {__mode = })
(character)
character
.(()
human = character:() character:(, )
human character ~= lp.Character
animator = human:() human:(, )
animator
animator = .()
animator.Parent = human
hookedAnimators[animator]
hookedAnimators[animator] =
(animator.AnimationPlayed:((animationTrack)
.(()
animationTrack animationTrack.IsPlaying (animationTrack)
)
))
_, tr (animator:())
(tr)
)
(lp.CharacterAdded:(hookAnimator))
lp.Character (lp.Character)
(rs.Heartbeat:(applyCurrentCharacterSpeeds))
(character, slotMap, persist)
character
animate = character:() character:(, )
human = character:()
animate human
human.RigType == .HumanoidRigType.R6
toast (, )
(() human:(human:()) )
_, tr (human:())
(() tr:(, ); tr:() )
animate.Disabled =
count =
slot, anims (slotMap)
folderName = m[slot]
folder = folderName animate:(folderName)
folder #anims >
_, o (folder:())
o:() o:()
saved = {}
_, a (anims)
na = .()
na.Name = a.Name
na.AnimationId = a.AnimationId
na.Parent = folder
saved[#saved + ] = {Name = a.Name, AnimationId = a.AnimationId}
persist equippedAnims[slot] = saved
count = count +
persist (EQUIPPED_FILE, equippedAnims)
.()
animate.Disabled =
count
(items)
char = lp.Character
char
slotMap, toLoad = {}, {}
_, slot (buttonOrder)
payload = items[slot]
payload
list = (payload, slot)
#list >
slotMap[slot] = list
_, a (list) toLoad[#toLoad + ] = a
(toLoad)
(char, slotMap, )
capturing = {}
(id)
key = (id)
capturing[key]
capturing[key] =
.(()
ok = (()
details = (id)
(details details.Items)
items = {}
_, it (details.Items)
items[#items + ] = {Id = it.Id, Name = it.Name, AssetType = it.AssetType, Type = it.Type}
anims, seen = {}, {}
slot, payload ((details.Items))
pid = (payload.Id)
seen[pid]
seen[pid] =
(payload.Id, id, payload.AssetType)
list = fileCache[pid]
(list) == #list >
copy = {}
_, a (list) copy[#copy + ] = {Name = a.Name, AnimationId = a.AnimationId}
anims[slot] = {Id = payload.Id, List = copy}
(e)
(e) == e.Items, e.Anims = items, anims
(savedBookmarks[key])
_, h (historyList)
(h.Id) == key (h)
(SAVED_BUNDLES_FILE, savedBookmarks)
(HISTORY_FILE, historyList)
)
capturing[key] =
)
(id, name)
id
key = (id)
oldItems, oldAnims
i = #historyList, , -
(historyList[i].Id) == key
oldItems, oldAnims = historyList[i].Items, historyList[i].Anims
.(historyList, i)
sv = savedBookmarks[key]
oldItems (sv) == oldItems, oldAnims = sv.Items, sv.Anims
mt = metaCache[key] {}
.(historyList, , {
Id = id, Name = name , Time = .(), Kind = mt.kind, Amount = mt.amount, Creator = mt.creator,
Items = oldItems, Anims = oldAnims,
})
#historyList > HISTORY_MAX .(historyList)
(HISTORY_FILE, historyList)
oldAnims (id)
onHistoryChanged ()
(bundleId, bundleName, onDone)
.(()
details = (bundleId)
details lp.Character
n = ((details.Items))
n >
(bundleId, bundleName)
lastPackData = {Id = bundleId, Name = bundleName}
(LAST_PACK_FILE, lastPackData)
toast (n > ( .. n .. ) , n > )
toast
(, )
onDone ()
)
(char)
char
.(()
human = char:(, )
animate = char:(, )
human animate
human.RigType == .HumanoidRigType.R6
toast (, )
animator = human:(, )
animator
animator = .()
animator.Parent = human
idleFolder = animate:()
idleFolder
idleAnimation = idleFolder:() idleFolder:()
idleAnimation
idleFolder:(, )
.()
char ~= lp.Character human.Health <=
(equippedAnims) ~=
(char, equippedAnims, )
)
lp.Character (lp.Character)
(lp.CharacterAdded:(initAutoEquip))
badgeRefs, fetchingMeta = {}, {}
(info)
price = (info.PriceInRobux)
price == ,
price , price
info.IsForSale == ,
info.IsForSale == info.IsLimited ~= info.IsLimitedUnique ~= ,
,
(entry)
(entry) ~= entry.Id
key = (entry.Id)
mt = metaCache[key] {}
mt.kind ==
rawStatus = entry.PriceStatus entry.ItemStatus
status = .((rawStatus))
price = (entry.Price)
price == price = (entry.LowestPrice)
status:() price ==
mt.kind, mt.amount = ,
status:() status:()
mt.kind =
price
mt.kind, mt.amount = , price
entry.CreatorName mt.creator mt.creator = (entry.CreatorName)
metaCache[key] = mt
mt
(id, needCreator)
key = (id)
mt = metaCache[key]
mt (mt.fetched (mt.kind ( needCreator mt.creator)))
fetchingMeta[key]
fetchingMeta[key] =
.(()
ok, info = (() mps:((id), .InfoType.Bundle) )
fetchingMeta[key] =
mm = metaCache[key] {}
mm.fetched =
ok (info) ==
mm.kind == mm.kind ==
k, a = (info)
mm.kind, mm.amount = k , a
mm.creator (info.Creator) == info.Creator.Name
mm.creator = (info.Creator.Name)
mm.kind ==
mm.kind =
metaCache[key] = mm
onMetaChanged (key)
)
(label, meta)
kind = meta meta.kind
text, bg, fg
kind == text, bg, fg = .. (meta.amount), C.green, .(, , )
kind == text, bg, fg = , C.accent2, .(, , )
kind == text, bg, fg = , C.surfaceHi, C.textDim
kind == text, bg, fg = , C.surfaceHi, C.textDim
text, bg, fg = , C.surfaceHi, C.textMuted
label.Text = text
(label, , {BackgroundColor3 = bg, TextColor3 = fg})
(label, id)
key = (id)
badgeRefs[key] = badgeRefs[key] {}
.(badgeRefs[key], label)
(label, metaCache[key])
(parent, props)
props = props {}
props.BackgroundColor3 = C.surfaceHi
props.AutomaticSize = .AutomaticSize.X
props.Size = .(, ())
props.Font = .Font.GothamBlack
props.TextSize = ()
props.TextColor3 = C.textMuted
props.Text =
props.BorderSizePixel =
b = (, props, parent)
(b); (b, (), ())
b
KEY_ALIASES = {
cat = , cats = ,
min = , max = ,
pricemin = , pricemax = ,
minprice = , maxprice = ,
}
VALID_KEYS = {sort = , price_min = , price_max = , price = , creator = , sale = , category = , sales = , period = , limit = , is = }
SORT_ALIASES = {
mostfavorited = , pricelowtohigh = , pricehightolow = , recentlycreated = ,
rel = , relevance = , relevant = ,
new = , newest = , recent = , latest = ,
fav = , favorite = , favorited = , popular = , top = ,
best = , bestselling = , sales = , seller = ,
low = , asc = , cheap = , cheapest = ,
high = , desc = , expensive = ,
}
SAVED_SORT = {
new = , newest = , recent = , latest = , old = , oldest = ,
name = , az = , alpha = , recentlycreated = , pricelowtohigh = , pricehightolow = , mostfavorited = ,
low = , asc = , cheap = , cheapest = , high = , desc = , expensive = ,
fav = , favorite = , favorites = ,
}
CAT_ALIASES = {
none = , all = , featured = , collectibles = , collectible = ,
community = , communitycreations = , ugc = , premium = , recommended = ,
}
SALES_ALIASES = {all = , collectibles = , collectible = , premium = }
PERIOD_ALIASES = {
[] = , day = , [] = , [] = , [] = ,
week = , [] = , month = , [] = , all = , alltime = ,
}
LIMITS = {, , , , }
(text)
text = text
tags, words = {}, {}
(k, v)
k = .(k)
k = KEY_ALIASES[k] k
VALID_KEYS[k]
i = #tags, , -
tags[i].key == k .(tags, i)
tags[#tags + ] = {key = k, val = v}
rest = text:(, (k, v)
(k, v)
)
word rest:()
k, v = word:()
k (k, v)
words[#words + ] = word
{keyword = .(words, ), tags = tags}
(q)
parts = {}
q.keyword ~= parts[#parts + ] = q.keyword
_, tg (q.tags)
v = tg.val
v:() v = .. v ..
parts[#parts + ] = tg.key .. .. v
.(parts, )
(tab, key, val)
key = KEY_ALIASES[key] key
v = (.(val):(, ))
saved = (tab ~= )
tab ==
key ==
x = ({new = , newest = , recent = , old = , oldest = , name = , az = , alpha = })[v]
x ~= , x
key ==
x = (saved SAVED_SORT SORT_ALIASES)[v]
x ~= , x
key == key ==
n = (v)
n n >= , .(n)
key ==
v == , {min = , max = }
a, b = v:()
a
a, b = (a), (b)
a > b a, b = b, a
, {min = a, max = b}
n = v:()
n n = (n); , {min = n, max = n}
key ==
val ~= , val
key ==
tab == (v == v == v == v == ) ,
saved (v == v == v == v == ) , (v == v)
saved
key ==
v == v == ,
v == v == ,
key ==
x = CAT_ALIASES[v]; x ~= , x
key ==
x = SALES_ALIASES[v]; x ~= , x
key ==
x = PERIOD_ALIASES[v]; x ~= , x
key ==
n = (v)
n
best = LIMITS[]
_, l (LIMITS)
.(l - n) < .(best - n) best = l
, best
(tab, q)
spec = {keyword = q.keyword, issues = {}}
_, tg (q.tags)
ok, v = (tab, tg.key, tg.val)
ok
spec.issues[#spec.issues + ] = tg.key .. .. tg.val
tg.key == spec.sort = v
tg.key == spec.min = v
tg.key == spec.max = v
tg.key == spec.min, spec.max = v.min, v.max
tg.key == spec.creator = v
tg.key == spec.saleOnly = (v == )
tg.key == spec.cat = v
tg.key == spec.sales = v
tg.key == spec.period = v
tg.key == spec.limit = v
tg.key == spec.is = v
spec
(spec)
p = CatalogSearchParams.()
(p, , spec.keyword )
(p, , {.BundleType.Animations})
priceFiltered = spec.min ~= spec.max ~=
(p, , ( spec.saleOnly) priceFiltered)
(p, , spec.limit )
freeOnly = spec.max ~= spec.max ==
sortName = spec.sort (freeOnly ) (((spec.keyword ) == ) )
e = (, sortName)
e (p, , e)
spec.min spec.min > (p, , spec.min)
spec.max spec.max > (p, , spec.max)
spec.creator (p, , spec.creator)
spec.cat
ce = (, spec.cat)
ce (p, , ce)
se = (, spec.sales )
se (p, , se)
spec.period
pe = (, spec.period)
pe (p, , pe)
p
priceTried = {}
(items)
queue = {}
_, entry (items)
(entry) == entry.Id
key = (entry.Id)
mt = metaCache[key]
mt entry.Kind
mt = {kind = entry.Kind, amount = entry.Amount, creator = entry.Creator}
metaCache[key] = mt
(mt mt.kind) mt.kind == mt = (entry)
( (mt mt.kind) mt.kind == ) priceTried[key]
queue[#queue + ] = key
#queue ==
nextI, running = ,
()
key = queue[nextI]
key
nextI +=
info
attempt = ,
ok, res = (() mps:((key), .InfoType.Bundle) )
ok (res) == info = res;
.()
info
priceTried[key] =
mm = metaCache[key] {}
k, a = (info)
mm.kind, mm.amount = k , a
mm.creator (info.Creator) == info.Creator.Name
mm.creator = (info.Creator.Name)
metaCache[key] = mm
running -=
_ = , .(, #queue)
running +=
.(worker)
t0 = .()
running > .() - t0 < .()
(entry, spec)
(entry) ~= entry.Id
mt = (entry)
wantPrice = spec (spec.min ~= spec.max ~= )
wantPrice
kind = mt mt.kind
amt
kind == amt =
kind == amt = mt.amount
amt ==
spec.min amt < spec.min
spec.max amt > spec.max
(v)
mt = metaCache[(v.Id)]
kind = (mt mt.kind) v.Kind
amount = (mt mt.amount) v.Amount
kind ==
kind == amount
(v, spec)
spec.is == v.Fav
spec.is == spec.is == spec.is ==
mt = metaCache[(v.Id)]
kind = (mt mt.kind) v.Kind
want = spec.is == spec.is
kind ~= want
(spec)
list = {}
kw = .(spec.keyword )
cr = spec.creator .(spec.creator)
key, v (savedBookmarks)
(v) == v.Id
metaCache[key] v.Kind
metaCache[key] = {kind = v.Kind, amount = v.Amount, creator = v.Creator}
ok =
kw ~= .(.(v.Name ), kw, , ) ok =
ok cr
mt = metaCache[key]
c = .((mt mt.creator) v.Creator )
.(c, cr, , ) ok =
ok (v, spec) ok =
ok (spec.min ~= spec.max ~= )
a = (v)
a == ok =
ok spec.min a < spec.min ok =
ok spec.max a > spec.max ok =
ok list[#list + ] = v
(a, b)
ta, tb = a.Time , b.Time
ta ~= tb ta > tb
(a.Id) < (b.Id)
mode = spec.sort
.(list, (a, b)
mode == (a, b)
mode ==
ta, tb = a.Time , b.Time
ta ~= tb ta < tb
(a.Id) < (b.Id)
mode ==
na, nb = .(a.Name ), .(b.Name )
na ~= nb na < nb
(a, b)
mode == mode ==
x, y = (a), (b)
x == y (a, b)
x ==
y ==
mode == x < y
x > y
fa, fb = a.Fav , b.Fav
fa ~= fb fa
(a, b)
)
list
(spec)
list = {}
kw = .(spec.keyword )
cr = spec.creator .(spec.creator)
_, v (historyList)
key = (v.Id)
metaCache[key] v.Kind
metaCache[key] = {kind = v.Kind, amount = v.Amount, creator = v.Creator}
ok =
kw ~= .(.(v.Name ), kw, , ) ok =
ok cr
mt = metaCache[key]
c = .((mt mt.creator) v.Creator )
.(c, cr, , ) ok =
ok (spec.min ~= spec.max ~= )
a = (v)
a == ok =
ok spec.min a < spec.min ok =
ok spec.max a > spec.max ok =
ok (v, spec) ok =
ok list[#list + ] = v
spec.sort ==
rev = {}
i = #list, , - rev[#rev + ] = list[i]
list = rev
spec.sort ==
.(list, (a, b) .(a.Name ) < .(b.Name ) )
spec.sort == spec.sort ==
asc = spec.sort ==
.(list, (a, b)
pa, pb = (a), (b)
pa == pb ==
pa ==
pb ==
pa == pb
asc pa < pb pa > pb
)
list
uiOk, uiErr = (()
PAD, TOPH = (), ()
W, H = (W0), (H0)
BODY_H = H - TOPH - PAD
LW, GAP = (), ()
RW = W - PAD * - LW - GAP
CHIP_Y, CHIP_H, TAG_Y, TAG_H, GRID_Y, FOOT_H = (), (), (), (), (), ()
GRID_H = BODY_H - GRID_Y - FOOT_H - ()
COLS, ROWS = ,
PER_PAGE =
CPAD = ()
CW = .((LW - CPAD * (COLS + )) / COLS)
GRID_AVAIL_H = GRID_H - CPAD * (ROWS + )
CH = .(.(GRID_AVAIL_H / ROWS), ())
AH = ()
playerGui = lp:(, ) lp:()
parentCandidates = {}
playerGui parentCandidates[#parentCandidates + ] = playerGui
(()
(gethui) ==
h = ()
h parentCandidates[#parentCandidates + ] = h
)
(() parentCandidates[#parentCandidates + ] = :() )
(()
playerGui playerGui:() playerGui.StudioAnimStudio:()
cg = :()
cg cg:() cg.StudioAnimStudio:()
)
g = .()
g.Name =
g.ResetOnSpawn =
g.IgnoreGuiInset =
g.ZIndexBehavior = .ZIndexBehavior.Sibling
g.DisplayOrder =
parented =
_, p (parentCandidates)
okP = (() g.Parent = p )
okP g.Parent == p parented = ;
parented
(() g.Parent = lp:() )
mf = (, {
Name = ,
Size = .(W, H),
AnchorPoint = .(, ),
Position = .(, ),
BackgroundColor3 = C.bg,
BackgroundTransparency = ,
BorderSizePixel = ,
ClipsDescendants = ,
Active = ,
Visible = ,
}, g)
(mf, ())
(mf, C.stroke, , )
topLine = (, {Size = .(, , , ()), BackgroundColor3 = C.accent2, BackgroundTransparency = , BorderSizePixel = , ZIndex = }, mf)
top = (, {Name = , Size = .(, , , TOPH), BackgroundTransparency = , Active = }, mf)
logo = (, {Size = .((), ()), Position = .(, PAD, , ), AnchorPoint = .(, ), BackgroundColor3 = C.surfaceHi, BackgroundTransparency = , BorderSizePixel = }, top)
(logo, ())
(, {Size = .(, ), BackgroundTransparency = , Text = , Font = .Font.GothamBlack, TextSize = (), TextColor3 = C.text}, logo)
(, {Position = .(PAD + (), ()), Size = .((), ()), BackgroundTransparency = , Text = , Font = .Font.GothamBlack, TextSize = (), TextColor3 = C.text, TextXAlignment = .TextXAlignment.Left}, top)
(, {Position = .(PAD + (), ()), Size = .((), ()), BackgroundTransparency = , Text = , Font = .Font.Gotham, TextSize = (), TextColor3 = C.textMuted, TextXAlignment = .TextXAlignment.Left}, top)
seg = (, {Position = .(PAD + (), .((TOPH - ()) / )), Size = .((), ()), BackgroundColor3 = C.surface, BackgroundTransparency = , BorderSizePixel = }, top)
(seg, ())
tabIndicator = (, {Position = .(, (), , ()), Size = .( / , -(), , -()), BackgroundColor3 = C.surfaceHi, BackgroundTransparency = , BorderSizePixel = , ZIndex = }, seg)
(tabIndicator, ())
tabDiscover = (seg, {Text = , Size = .( / , ), ZIndex = , TextSize = ()}, C.surface, , C.text)
tabSaved = (seg, {Text = , Size = .( / , ), Position = .( / , ), ZIndex = , TextSize = ()}, C.surface, , C.textMuted)
tabRecent = (seg, {Text = , Size = .( / , ), Position = .( / , ), ZIndex = , TextSize = ()}, C.surface, , C.textMuted)
tabPresets = (seg, {Text = , Size = .( / , ), Position = .( / , ), ZIndex = , TextSize = ()}, C.surface, , C.textMuted)
closeBtn = (top, {Text = , Size = .((), ()), Position = .(, -PAD - (), , ), AnchorPoint = .(, ), TextSize = ()}, C.surface, , C.textDim)
(closeBtn, ())
minBtn = (top, {Text = , Size = .((), ()), Position = .(, -PAD - (), , ), AnchorPoint = .(, ), TextSize = ()}, C.surface, , C.textDim)
(minBtn, ())
left = (, {Position = .(PAD, TOPH), Size = .(LW, BODY_H), BackgroundTransparency = }, mf)
helpW, searchW, gap4 = (), (), ()
sbW = LW - helpW - searchW - gap4 *
sbBox = (, {Size = .(sbW, ()), BackgroundColor3 = C.surface, BackgroundTransparency = , BorderSizePixel = }, left)
(sbBox, ())
(sbBox, C.stroke, , )
sb = (, {
Size = .(, -(), , ), Position = .((), ), BackgroundTransparency = , Text = ,
PlaceholderText = ,
ClearTextOnFocus = , Font = .Font.Gotham, TextSize = (),
TextColor3 = C.text, PlaceholderColor3 = C.textMuted, TextXAlignment = .TextXAlignment.Left, ClipsDescendants = ,
}, sbBox)
clearBtn = (sbBox, {Text = , Size = .((), ()), Position = .(, -(), , ), AnchorPoint = .(, ), TextSize = (), Visible = }, C.surfaceHi, , C.textDim)
(clearBtn)
searchBtn = (left, {Text = , Size = .(searchW, ()), Position = .(sbW + gap4, ), TextSize = ()}, C.surfaceHi, , C.text)
(searchBtn, ())
helpBtn = (left, {Text = , Size = .(helpW, ()), Position = .(sbW + gap4 * + searchW, ), TextSize = ()}, C.surface, , C.textDim)
(helpBtn, ())
settingsBtn = (top, {Text = , Size = .((), ()), Position = .(, -PAD - (), , ), AnchorPoint = .(, ), TextSize = ()}, C.surface, , C.textDim)
(settingsBtn, ())
(y, h)
row = (, {
Position = .(, y), Size = .(LW, h), BackgroundTransparency = , BorderSizePixel = ,
ScrollBarThickness = , CanvasSize = .(, , , ), AutomaticCanvasSize = .AutomaticSize.X,
ScrollingDirection = .ScrollingDirection.X, ZIndex = ,
}, left)
(, {FillDirection = .FillDirection.Horizontal, Padding = .(, ()), SortOrder = .SortOrder.LayoutOrder, VerticalAlignment = .VerticalAlignment.Center}, row)
row
chipsRow = (CHIP_Y, CHIP_H)
tagsRow = (TAG_Y, TAG_H)
tagHint = (, {
Name = ,
Position = .(, TAG_Y),
Size = .(LW, TAG_H),
BackgroundTransparency = ,
BorderSizePixel = ,
Text = ,
Font = .Font.GothamBold,
TextScaled = ,
TextColor3 = C.textDim,
TextXAlignment = .TextXAlignment.Center,
TextYAlignment = .TextYAlignment.Center,
TextWrapped = ,
ZIndex = ,
}, left)
(, {MaxTextSize = (), MinTextSize = ()}, tagHint)
gridHolder = (, {Position = .(, GRID_Y), Size = .(LW, GRID_H), BackgroundColor3 = C.panel, BackgroundTransparency = , BorderSizePixel = , ClipsDescendants = }, left)
(gridHolder, ()); (gridHolder, C.stroke, , )
(, {
CellSize = .(CW, CH),
CellPadding = .(CPAD, CPAD),
SortOrder = .SortOrder.LayoutOrder,
FillDirectionMaxCells = COLS,
HorizontalAlignment = .HorizontalAlignment.Center,
VerticalAlignment = .VerticalAlignment.Center,
}, gridHolder)
gridMsg = (, {Size = .(, ), BackgroundTransparency = , Text = , Font = .Font.GothamSemibold, TextSize = (), TextColor3 = C.textMuted, Visible = , ZIndex = }, left)
gridMsg.Position = .(, GRID_Y)
gridMsg.Size = .(LW, GRID_H)
helpPanel = (, {Position = .(, GRID_Y), Size = .(LW, GRID_H), BackgroundColor3 = C.overlay, BackgroundTransparency = , BorderSizePixel = , Visible = , ClipsDescendants = , ZIndex = }, left)
(helpPanel, ())
helpScroll = (, {Size = .(, ), BackgroundTransparency = , BorderSizePixel = , ScrollBarThickness = (), ScrollBarImageColor3 = C.surfaceHi, CanvasSize = .(, , , ), AutomaticCanvasSize = .AutomaticSize.Y, ZIndex = }, helpPanel)
HELP_TEXT = .({
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
}, )
helpText = (, {
Position = .((), ()), Size = .(, -(), , ), AutomaticSize = .AutomaticSize.Y, BackgroundTransparency = ,
RichText = , Text = HELP_TEXT, TextWrapped = , Font = .Font.Gotham, TextSize = (), TextColor3 = C.text, TextTransparency = ,
TextXAlignment = .TextXAlignment.Left, TextYAlignment = .TextYAlignment.Top, ZIndex = ,
}, helpScroll)
settingsPanel = (, {Position = .(, GRID_Y), Size = .(LW, GRID_H), BackgroundColor3 = C.overlay, BackgroundTransparency = , BorderSizePixel = , Visible = , ClipsDescendants = , ZIndex = }, left)
(settingsPanel, ())
(settingsPanel, C.stroke, , )
setScroll = (, {Size = .(, ), BackgroundTransparency = , BorderSizePixel = , ScrollBarThickness = (), ScrollBarImageColor3 = C.surfaceHi, CanvasSize = .(, , , ), AutomaticCanvasSize = .AutomaticSize.Y, ZIndex = }, settingsPanel)
(setScroll, (), (), (), ())
(text, y)
lbl = (, {Position = .(, y), Size = .(, , , ()), BackgroundTransparency = , Text = text, Font = .Font.GothamBold, TextSize = (), TextColor3 = C.text, TextXAlignment = .TextXAlignment.Left, ZIndex = }, setScroll)
lbl
(parent, label, minVal, maxVal, currentVal, yPos, callback)
row = (, {Position = .(, yPos), Size = .(, , , ()), BackgroundTransparency = , ZIndex = }, parent)
label ~=
(, {Size = .(, , , ()), BackgroundTransparency = , Text = label, Font = .Font.Gotham, TextSize = (), TextColor3 = C.textDim, TextXAlignment = .TextXAlignment.Left, ZIndex = }, row)
bg = (, {Position = .(, label ~= () ()), Size = .(, , , ()), BackgroundColor3 = C.surfaceHi, ZIndex = }, row)
(bg)
pct = (currentVal - minVal) / (maxVal - minVal)
fill = (, {Size = .(pct, , , ), BackgroundColor3 = C.accent2, ZIndex = }, bg)
(fill)
knob = (, {Size = .((), ()), AnchorPoint = .(, ), Position = .(pct, , , ), BackgroundColor3 = WHITE, ZIndex = }, bg)
(knob)
valLbl = (, {Position = .(, , , label ~= () ()), Size = .(, , , ()), BackgroundTransparency = , Text = .(, currentVal), Font = .Font.GothamBold, TextSize = (), TextColor3 = C.text, TextXAlignment = .TextXAlignment.Left, ZIndex = }, row)
dragging =
knob.InputBegan:((input)
input.UserInputType == .UserInputType.MouseButton1 input.UserInputType == .UserInputType.Touch dragging =
)
(uis.InputEnded:((input)
input.UserInputType == .UserInputType.MouseButton1 input.UserInputType == .UserInputType.Touch dragging =
))
(uis.InputChanged:((input)
dragging (input.UserInputType == .UserInputType.MouseMovement input.UserInputType == .UserInputType.Touch)
posX = .(input.Position.X - bg.AbsolutePosition.X, , bg.AbsoluteSize.X)
p = posX / bg.AbsoluteSize.X
val = minVal + p * (maxVal - minVal)
fill.Size = .(p, , , )
knob.Position = .(p, , , )
valLbl.Text = .(, val)
(val)
))
row
(, ())
restoreToggle = (setScroll, {Text = scriptSettings.restoreLastPack , Size = .((), ()), Position = .(, ()), TextSize = (), ZIndex = }, scriptSettings.restoreLastPack C.accent2 C.surface, , scriptSettings.restoreLastPack .(,,) C.textDim)
(restoreToggle, ())
restoreToggle.MouseButton1Click:(()
scriptSettings.restoreLastPack = scriptSettings.restoreLastPack
restoreToggle.Text = scriptSettings.restoreLastPack
(restoreToggle, scriptSettings.restoreLastPack C.accent2 C.surface, , scriptSettings.restoreLastPack and Color3.new(0,0,0) or C.textDim)
saveSettings()
end)
addSettingLabel("Force Animation (Ignore Tools)", s(85))
local forceToggle = mkBtn(setScroll, {Text = scriptSettings.forceAnimation and "ON" or "OFF", Size = UDim2.fromOffset(s(50), s(25)), Position = UDim2.fromOffset(0, s(115)), TextSize = s(14), ZIndex = 22}, scriptSettings.forceAnimation and C.accent2 or C.surface, 0, scriptSettings.forceAnimation and Color3.new(0,0,0) or C.textDim)
corner(forceToggle, s(6))
forceToggle.MouseButton1Click:Connect(function()
scriptSettings.forceAnimation = not scriptSettings.forceAnimation
forceToggle.Text = scriptSettings.forceAnimation and "ON" or "OFF"
setBtn(forceToggle, scriptSettings.forceAnimation and C.accent2 or C.surface, 0, scriptSettings.forceAnimation and Color3.new(0,0,0) or C.textDim)
saveSettings()
end)
addSettingLabel("Global Animation Speed", s(160))
createSlider(setScroll, "", 0.25, 5, scriptSettings.globalSpeed, s(180), function(val)
scriptSettings.globalSpeed = val
saveSettings()
applyCurrentCharacterSpeeds()
end)
addSettingLabel("Advance Speed Animation", s(230))
local advToggle = mkBtn(setScroll, {Text = scriptSettings.advanceSpeed and "ON" or "OFF", Size = UDim2.fromOffset(s(50), s(25)), Position = UDim2.fromOffset(0, s(255)), TextSize = s(14), ZIndex = 22}, scriptSettings.advanceSpeed and C.accent2 or C.surface, 0, scriptSettings.advanceSpeed and Color3.new(0,0,0) or C.textDim)
corner(advToggle, s(6))
local customSpeedRows = {}
local customSpeedsY = s(290)
local function createCustomSliders()
for _, row in ipairs(customSpeedRows) do row:Destroy() end
table.clear(customSpeedRows)
customSpeedsY = s(290)
if scriptSettings.advanceSpeed then
for _, slot in ipairs(buttonOrder) do
local currentVal = scriptSettings.customSpeeds[slot] or 1
local row = createSlider(setScroll, shortNames[slot] .. " Speed", 0.25, 5, currentVal, customSpeedsY, function(val)
scriptSettings.customSpeeds[slot] = val
saveSettings()
applyCurrentCharacterSpeeds()
end)
customSpeedRows[#customSpeedRows + 1] = row
customSpeedsY += s(45)
end
end
end
createCustomSliders()
advToggle.MouseButton1Click:Connect(function()
scriptSettings.advanceSpeed = not scriptSettings.advanceSpeed
advToggle.Text = scriptSettings.advanceSpeed and "ON" or "OFF"
setBtn(advToggle, scriptSettings.advanceSpeed and C.accent2 or C.surface, 0, scriptSettings.advanceSpeed and Color3.new(0,0,0) or C.textDim)
saveSettings()
createCustomSliders()
applyCurrentCharacterSpeeds()
end)
local footer = new("Frame", {Position = UDim2.fromOffset(0, BODY_H - FOOT_H), Size = UDim2.fromOffset(LW, FOOT_H), BackgroundTransparency = 1}, left)
local prevBtn = mkBtn(footer, {Text = "← Back", Size = UDim2.fromOffset(s(90), FOOT_H), TextSize = s(14)}, C.surfaceHi, 0.6, C.textMuted)
corner(prevBtn, s(8))
local nextBtn = mkBtn(footer, {Text = "Next →", Size = UDim2.fromOffset(s(90), FOOT_H), Position = UDim2.new(1, -s(90), 0, 0), TextSize = s(14)}, C.surfaceHi, 0.6, C.textMuted)
corner(nextBtn, s(8))
local pageLbl = new("TextLabel", {Position = UDim2.fromOffset(s(94), 0), Size = UDim2.new(1, -s(188), 1, 0), BackgroundTransparency = 1, Text = "Loading…", Font = Enum.Font.GothamSemibold, TextSize = s(14), TextColor3 = C.textDim, TextXAlignment = Enum.TextXAlignment.Center}, footer)
local rightP = new("Frame", {Position = UDim2.fromOffset(PAD + LW + GAP, TOPH), Size = UDim2.fromOffset(RW, BODY_H), BackgroundColor3 = C.panel, BackgroundTransparency = 0.5, BorderSizePixel = 0}, mf)
corner(rightP, s(14)); stroke(rightP, C.stroke, 1, 0.6)
local IP = s(10)
local innerW = RW - IP * 2
local VPH = s(140)
local masterViewport = new("ViewportFrame", {
Position = UDim2.fromOffset(IP, IP), Size = UDim2.fromOffset(innerW, VPH), BackgroundColor3 = C.overlay, BackgroundTransparency = 0.15, BorderSizePixel = 0,
Ambient = Color3.fromRGB(160, 160, 165), LightColor = WHITE, LightDirection = Vector3.new(-1, -1.5, -1), ClipsDescendants = true,
}, rightP)
corner(masterViewport, s(12)); stroke(masterViewport, C.stroke, 1, 0.5)
local hintLabel = new("TextLabel", {Size = UDim2.fromScale(1, 1), BackgroundTransparency = 1, Text = "Select a bundle\nto preview it here", Font = Enum.Font.GothamSemibold, TextSize = s(15), TextColor3 = C.textMuted, ZIndex = 3}, masterViewport)
local previewShimmer = new("Frame", {Size = UDim2.fromScale(1, 1), BackgroundColor3 = C.surfaceHi, BackgroundTransparency = 0.5, BorderSizePixel = 0, Visible = false, ZIndex = 5}, masterViewport)
corner(previewShimmer, s(12)); applyShimmer(previewShimmer, C.overlay, C.surface)
local masterBadge = mkBadge(masterViewport, {Position = UDim2.fromOffset(s(10), s(10)), ZIndex = 6, Visible = false})
local statusChip = new("TextLabel", {
Position = UDim2.fromOffset(s(10), VPH - s(34)), Size = UDim2.fromOffset(0, s(24)), AutomaticSize = Enum.AutomaticSize.X,
BackgroundColor3 = C.overlay, BackgroundTransparency = 0.3, BorderSizePixel = 0, Text = "NO BUNDLE", Font = Enum.Font.GothamBold, TextSize = s(13), TextColor3 = C.textMuted, ZIndex = 6,
}, masterViewport)
round(statusChip); pad(statusChip, s(12), s(12))
local yInfo = IP + VPH + s(6)
local nameLabel = new("TextLabel", {Position = UDim2.fromOffset(IP, yInfo), Size = UDim2.fromOffset(innerW - s(42), s(22)), BackgroundTransparency = 1, Text = "No bundle selected", Font = Enum.Font.GothamBold, TextSize = s(17), TextColor3 = C.text, TextXAlignment = Enum.TextXAlignment.Left, TextTruncate = Enum.TextTruncate.AtEnd}, rightP)
local creatorLabel = new("TextLabel", {Position = UDim2.fromOffset(IP, yInfo + s(24)), Size = UDim2.fromOffset(innerW - s(42), s(17)), BackgroundTransparency = 1, Text = "Pick one from the list", Font = Enum.Font.Gotham, TextSize = s(13), TextColor3 = C.textDim, TextXAlignment = Enum.TextXAlignment.Left, TextTruncate = Enum.TextTruncate.AtEnd}, rightP)
local starBtn = mkBtn(rightP, {Text = "☆", Size = UDim2.fromOffset(s(38), s(38)), Position = UDim2.fromOffset(IP + innerW - s(38), yInfo), TextSize = s(22), Visible = false}, C.surface, 0.2, C.textDim)
corner(starBtn, s(9))
local ySlots = yInfo + s(44) + s(4)
local slotGrid = new("Frame", {Position = UDim2.fromOffset(IP, ySlots), Size = UDim2.fromOffset(innerW, s(60)), BackgroundTransparency = 1}, rightP)
local cellW = math.floor((innerW - s(6) * 3) / 4)
new("UIGridLayout", {CellSize = UDim2.fromOffset(cellW, s(28)), CellPadding = UDim2.fromOffset(s(6), s(6)), SortOrder = Enum.SortOrder.LayoutOrder}, slotGrid)
local slotButtons = {}
for i, slot in ipairs(buttonOrder) do
local b = mkBtn(slotGrid, {Text = shortNames[slot], LayoutOrder = i, TextSize = s(14)}, C.surface, 0.5, C.textMuted)
corner(b, s(8))
slotButtons[slot] = b
end
local yAct = ySlots + s(60) + s(8)
local halfW = math.floor((innerW - s(10)) / 2)
local wearSelBtn = mkBtn(rightP, {Text = "Wear Selected", Size = UDim2.fromOffset(halfW, s(34)), Position = UDim2.fromOffset(IP, yAct), TextSize = s(14)}, C.surfaceHi, 0.55, C.textMuted)
corner(wearSelBtn, s(9))
local wearAllBtn = mkBtn(rightP, {Text = "Wear All", Size = UDim2.fromOffset(halfW, s(34)), Position = UDim2.fromOffset(IP + halfW + s(10), yAct), TextSize = s(14)}, C.surfaceHi, 0.55, C.textMuted)
corner(wearAllBtn, s(9))
local resetBtn = mkBtn(rightP, {Text = "RESET TO DEFAULT", Size = UDim2.fromOffset(innerW, s(34)), Position = UDim2.fromOffset(IP, yAct + s(34) + s(8)), TextSize = s(14), Font = Enum.Font.GothamBold}, Color3.fromRGB(160, 80, 80), 0.1, WHITE)
corner(resetBtn, s(8))
local toastLbl = new("TextLabel", {
AnchorPoint = Vector2.new(0.5, 1), Position = UDim2.new(0.5, 0, 1, -s(18)), Size = UDim2.fromOffset(0, s(34)), AutomaticSize = Enum.AutomaticSize.X,
BackgroundColor3 = C.panel, BackgroundTransparency = 1, BorderSizePixel = 0, Text = "", Font = Enum.Font.GothamSemibold, TextSize = s(15),
TextColor3 = C.text, TextTransparency = 1, Visible = false, ZIndex = 100,
}, mf)
round(toastLbl); pad(toastLbl, s(18), s(18))
local toastStroke = stroke(toastLbl, C.accent2, 1, 1)
local pill = mkBtn(g, {Text = "UGC", Size = UDim2.fromOffset(s(58), s(58)), AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.new(1, -s(50), 0, s(76)), TextSize = s(17), Font = Enum.Font.GothamBlack, Visible = false, Active = true}, C.surfaceHi, 0.1, C.text)
round(pill); stroke(pill, C.accent2, 1, 0.5)
local currentTab = "Discover"
local searchResults, savedTabList = {}, {}
local catalogCursor
local currentPageIndex = 1
local activeSpec
local activeBundle
local currentBundleItems = {}
local selectedSlot = "idleanimation"
local searchGen, masterGen, inspectGen = 0, 0, 0
local masterTrack, masterRig, masterAnimator
local cards, skeletons = {}, {}
local chipButtons, tagPills = {}, {}
local pagingBusy, wearBusy = false, false
local inspectBundle, playMaster, runSearch, refreshTags, drawGridPage, refreshSavedView
local toastGen = 0
toast = function(text, kind)
toastGen = toastGen + 1
local myGen = toastGen
local col = (kind == "bad" and C.red) or (kind == "ok" and C.accent2) or C.accent2
toastLbl.Text = text
toastStroke.Color = col
toastLbl.Visible = true
toastLbl.Position = UDim2.new(0.5, 0, 1, -s(2))
toastLbl.BackgroundTransparency, toastLbl.TextTransparency, toastStroke.Transparency = 1, 1, 1
tw(toastLbl, 0.28, {Position = UDim2.new(0.5, 0, 1, -s(18)), BackgroundTransparency = 0.15, TextTransparency = 0}, Enum.EasingStyle.Back)
tw(toastStroke, 0.28, {Transparency = 0.3})
task.delay(2.4, function()
if myGen ~= toastGen then return end
tw(toastLbl, 0.3, {BackgroundTransparency = 1, TextTransparency = 1})
tw(toastStroke, 0.3, {Transparency = 1})
task.delay(0.32, function()
if myGen == toastGen then toastLbl.Visible = false end
end)
end)
end
local spinning = {}
local spinAngle = 0
local spinConn
spinConn = rs.RenderStepped:Connect(function(dt)
if not g.Parent then spinConn:Disconnect(); return end
spinAngle = (spinAngle + math.rad(28) * dt) % (math.pi * 2)
local cf = CFrame.Angles(0, spinAngle, 0)
for model in pairs(spinning) do
if model.Parent then model:PivotTo(cf) else spinning[model] = nil end
end
end)
track(spinConn)
local function buildRig(vpFrame)
local wm = vpFrame:FindFirstChildOfClass("WorldModel")
if wm then wm:ClearAllChildren() else wm = new("WorldModel", {}, vpFrame) end
local char = lp.Character or lp.CharacterAdded:Wait()
local wasArch = char.Archivable
char.Archivable = true
local clone = char:Clone()
char.Archivable = wasArch
if not clone then return nil end
for _, d in ipairs(clone:GetDescendants()) do
if d:IsA("LuaSourceContainer") or d:IsA("Tool") or d:IsA("Sound") then d:Destroy() end
end
local root = clone:FindFirstChild("HumanoidRootPart")
local human = clone:FindFirstChildOfClass("Humanoid")
if not root or not human then clone:Destroy(); return nil end
root.Anchored = true
pcall(function()
human.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.None
human.HealthDisplayType = Enum.HumanoidHealthDisplayType.AlwaysOff
end)
local animator = human:FindFirstChildOfClass("Animator") or new("Animator", {}, human)
clone.Parent = wm
clone:PivotTo(CFrame.new(0, 0, 0))
local cam = vpFrame.CurrentCamera
if not cam then
cam = new("Camera", {}, vpFrame)
vpFrame.CurrentCamera = cam
end
cam.FieldOfView = 50
cam.CFrame = CFrame.lookAt(Vector3.new(0, 0.6, 8), Vector3.new(0, -0.2, 0))
spinning[clone] = true
return clone, animator
end
local function tryPlayTrack(animator, animation, looped)
local ok, pt = pcall(function()
local tr = animator:LoadAnimation(animation)
tr.Looped = looped
tr:Play()
return tr
end)
if not ok or not pt then return false, nil end
task.wait(0.15)
if not pt.IsPlaying then
pcall(function() pt:Stop() end)
return false, nil
end
return true, pt
end
local function setPreviewState(state, slot, n)
local label = (slot and shortNames[slot]) and string.upper(shortNames[slot]) or ""
if state == "empty" then
statusChip.Text, statusChip.TextColor3 = "NO BUNDLE", C.textMuted
previewShimmer.Visible = false
hintLabel.Visible = true
elseif state == "loading" then
statusChip.Text, statusChip.TextColor3 = label .. " · LOADING", C.textDim
previewShimmer.Visible = true
elseif state == "retry" then
statusChip.Text, statusChip.TextColor3 = label .. " · RETRY " .. tostring(n or 1), C.orange
previewShimmer.Visible = true
elseif state == "playing" then
statusChip.Text, statusChip.TextColor3 = "• " .. label, C.accent2
previewShimmer.Visible = false
elseif state == "missing" then
statusChip.Text, statusChip.TextColor3 = "NO ANIMATIONS", C.orange
previewShimmer.Visible = false
else
statusChip.Text, statusChip.TextColor3 = label .. " · FAILED", C.red
previewShimmer.Visible = false
end
end
local function refreshSlotButtons()
for slot, b in pairs(slotButtons) do
local has = currentBundleItems[slot] ~= nil
if has and slot == selectedSlot then setBtn(b, C.surfaceHi, 0, C.text)
elseif has then setBtn(b, C.surface, 0.2, C.text)
else setBtn(b, C.surface, 0.5, C.textMuted) end
end
end
local function setActionsEnabled(on)
if on then
setBtn(wearSelBtn, C.surfaceHi, 0.15, C.text)
setBtn(wearAllBtn, C.surfaceHi, 0.15, C.text)
else
setBtn(wearSelBtn, C.surfaceHi, 0.55, C.textMuted)
setBtn(wearAllBtn, C.surfaceHi, 0.55, C.textMuted)
end
end
local function refreshActiveInfo()
if not activeBundle then
nameLabel.Text = "No bundle selected"
creatorLabel.Text = "Pick one from the list"
masterBadge.Visible = false
starBtn.Visible = false
return
end
if activeBundle.IsPreset then
nameLabel.Text = activeBundle.Name
creatorLabel.Text = "Preset · " .. tostring(activeBundle.Count or 0) .. ((activeBundle.Count or 0) == 1 and " slot" or " slots")
masterBadge.Visible = false
starBtn.Visible = false
return
end
local key = tostring(activeBundle.Id)
local meta = metaCache[key]
nameLabel.Text = activeBundle.Name
local parts = {}
if meta and meta.creator then parts[#parts + 1] = "by " .. meta.creator end
parts[#parts + 1] = "ID " .. key
creatorLabel.Text = table.concat(parts, " · ")
masterBadge.Visible = true
applyBadge(masterBadge, meta)
starBtn.Visible = true
local saved = savedBookmarks[key] ~= nil
starBtn.Text = saved and "★" or "☆"
starBtn.TextColor3 = saved and C.gold or C.textDim
end
onMetaChanged = function(key)
local refs = badgeRefs[key]
if refs then
for i = #refs, 1, -1 do
local lbl = refs[i]
if lbl.Parent then applyBadge(lbl, metaCache[key]) else table.remove(refs, i) end
end
end
if activeBundle and tostring(activeBundle.Id) == key then refreshActiveInfo() end
local bm = savedBookmarks[key]
local mt = metaCache[key]
if bm and mt then
bm.Kind, bm.Amount, bm.Creator = mt.kind, mt.amount, mt.creator
queueSave(SAVED_BUNDLES_FILE, savedBookmarks)
end
if mt then
local touched = false
for _, h in ipairs(historyList) do
if tostring(h.Id) == key then
h.Kind, h.Amount, h.Creator = mt.kind, mt.amount, mt.creator
touched = true
end
end
if touched then queueSave(HISTORY_FILE, historyList) end
end
end
local function updateSelection()
for _, c in ipairs(cards) do
if c.box.Parent then c.restore() end
end
end
local function runMasterPlayback(myGen, payload, slot, tracks)
local refetched = false
local idx, fails = 1, 0
while myGen == masterGen do
local tr = tracks[idx]
local ok, played = false, nil
if tr then ok, played = tryPlayTrack(masterAnimator, tr, #tracks == 1) end
if myGen ~= masterGen then
if played then pcall(function() played:Stop(0) end) end
return nil
end
if ok then
fails = 0
masterTrack = played
applySpeed(masterTrack, slot)
setPreviewState("playing", slot)
if #tracks == 1 then return true end
played.Stopped:Wait()
idx = idx % #tracks + 1
else
fails = fails + 1
if fails >= 2 * math.max(#tracks, 1) then
if refetched then return false end
refetched, fails = true, 0
clearAssetCache(payload.Id)
task.wait(0.4)
if myGen ~= masterGen then return nil end
tracks = getTracksFor(payload, slot)
idx = 1
if #tracks == 0 then return false end
else
idx = idx % math.max(#tracks, 1) + 1
task.wait(0.3)
end
end
end
return nil
end
playMaster = function(slot)
local payload = currentBundleItems[slot]
if not payload then return end
masterGen = masterGen + 1
local myGen = masterGen
if masterTrack then pcall(function() masterTrack:Stop(0.1) end); masterTrack = nil end
setPreviewState("loading", slot)
task.spawn(function()
if not masterAnimator or not masterRig or not masterRig.Parent then
masterRig, masterAnimator = buildRig(masterViewport)
end
if myGen ~= masterGen then return end
if not masterAnimator then setPreviewState("failed", slot); return end
local tracks = {}
for attempt = 0, 4 do
tracks = getTracksFor(payload, slot)
if #tracks > 0 then break end
if myGen ~= masterGen then return end
setPreviewState("retry", slot, attempt + 1)
clearAssetCache(payload.Id)
task.wait(math.min(0.5 + attempt * 0.3, 2.5))
if myGen ~= masterGen then return end
end
if myGen ~= masterGen then return end
if #tracks == 0 then setPreviewState("failed", slot); return end
local result = runMasterPlayback(myGen, payload, slot, tracks)
if result == false and myGen == masterGen then setPreviewState("failed", slot) end
end)
end
inspectBundle = function(entry)
activeBundle = {Id = entry.Id, Name = entry.Name or "Unknown"}
inspectGen = inspectGen + 1
local myInspect = inspectGen
masterGen = masterGen + 1
if masterTrack then pcall(function() masterTrack:Stop(0.1) end); masterTrack = nil end
table.clear(currentBundleItems)
hintLabel.Visible = false
seedMeta(entry)
ensureMeta(entry.Id, true)
refreshActiveInfo()
refreshSlotButtons()
setActionsEnabled(false)
setPreviewState("loading", selectedSlot)
updateSelection()
task.spawn(function()
local details = getDetails(entry.Id)
if myInspect ~= inspectGen then return end
if not details then setPreviewState("failed", selectedSlot); return end
for k, v in pairs(mapBundleItems(details.Items)) do currentBundleItems[k] = v end
local slot = currentBundleItems[selectedSlot] and selectedSlot or nil
if not slot then
for _, name in ipairs(buttonOrder) do
if currentBundleItems[name] then slot = name; break end
end
end
if not slot then
refreshSlotButtons()
setPreviewState("missing", selectedSlot)
return
end
selectedSlot = slot
refreshSlotButtons()
setActionsEnabled(true)
masterRig, masterAnimator = nil, nil
playMaster(slot)
end)
end
for _, slot in ipairs(buttonOrder) do
slotButtons[slot].MouseButton1Click:Connect(function()
if not activeBundle or not currentBundleItems[slot] then return end
selectedSlot = slot
refreshSlotButtons()
playMaster(slot)
end)
end
wearSelBtn.MouseButton1Click:Connect(function()
if not activeBundle or wearBusy then return end
local payload = currentBundleItems[selectedSlot]
if not payload then return end
if not lp.Character then toast("No character found", "bad"); return end
wearBusy = true
wearSelBtn.Text = "Equipping…"
local slot = selectedSlot
task.spawn(function()
local list = getTracksFor(payload, slot)
if #list > 0 then
preloadAnimations(list)
local n = equipMap(lp.Character, {[slot] = list}, true)
if n > 0 and activeBundle and not activeBundle.IsPreset then
recordHistory(activeBundle.Id, activeBundle.Name)
lastPackData = {Id = activeBundle.Id, Name = activeBundle.Name}
saveJSON(LAST_PACK_FILE, lastPackData)
end
toast(n > 0 and ("Equipped " .. shortNames[slot]) or "Couldn't equip that slot", n > 0 and "ok" or "bad")
else
toast("Animation failed to load", "bad")
end
wearSelBtn.Text = "Wear Selected"
wearBusy = false
end)
end)
wearAllBtn.MouseButton1Click:Connect(function()
if not activeBundle or wearBusy or next(currentBundleItems) == nil then return end
if not lp.Character then toast("No character found", "bad"); return end
wearBusy = true
wearAllBtn.Text = "Equipping…"
task.spawn(function()
local n = applyBundleItemsToCharacter(currentBundleItems)
if n > 0 and activeBundle and not activeBundle.IsPreset then
recordHistory(activeBundle.Id, activeBundle.Name)
lastPackData = {Id = activeBundle.Id, Name = activeBundle.Name}
saveJSON(LAST_PACK_FILE, lastPackData)
end
toast(n > 0 and ("Equipped " .. n .. " animations") or "Nothing could be equipped", n > 0 and "ok" or "bad")
wearAllBtn.Text = "Wear All"
wearBusy = false
end)
end)
local resetBusy = false
resetBtn.MouseButton1Click:Connect(function()
if resetBusy then return end
resetBusy = true
resetBtn.Text = "RESETTING..."
task.spawn(function()
local function finishReset()
if resetBtn and resetBtn.Parent then resetBtn.Text = "RESET TO DEFAULT" end
resetBusy = false
end
for k in pairs(equippedAnims) do equippedAnims[k] = nil end
saveJSON(EQUIPPED_FILE, equippedAnims)
lastPackData = {}
saveJSON(LAST_PACK_FILE, lastPackData)
local char = lp.Character
local human = char and char:FindFirstChildOfClass("Humanoid")
if not char or not human then
toast("No character found", "bad")
finishReset()
return
end
local okDesc, desc = pcall(function()
return plrs:GetHumanoidDescriptionFromUserId(lp.UserId)
end)
if not okDesc or not desc then
okDesc, desc = pcall(function() return human:GetAppliedDescription() end)
end
if not okDesc or not desc then
toast("Couldn't retrieve default avatar animations", "bad")
finishReset()
return
end
pcall(function() human:ApplyDescription(desc) end)
local animate = char:FindFirstChild("Animate") or char:WaitForChild("Animate", 5)
if not animate then
pcall(function() desc:Destroy() end)
toast("Animate script not found", "bad")
finishReset()
return
end
for _, tr in ipairs(human:GetPlayingAnimationTracks()) do
pcall(function() tr:Stop(0) end)
end
pcall(function() animate.Disabled = true end)
local defaults = {
idle = {"507766666", "507766951"},
walk = {"507777826"},
run = {"507767714"},
jump = {"507765000"},
fall = {"507767968"},
climb = {"507765644"},
swim = {"507784897"},
swimidle = {"507785072"},
}
local descriptionProperties = {
idle = "IdleAnimation", walk = "WalkAnimation", run = "RunAnimation",
jump = "JumpAnimation", fall = "FallAnimation", climb = "ClimbAnimation",
swim = "SwimAnimation", swimidle = "SwimAnimation",
}
local restored = 0
for folderName, fallbackIds in pairs(defaults) do
local folder = animate:FindFirstChild(folderName)
if folder then
local ids = fallbackIds
local property = descriptionProperties[folderName]
if property and desc then
local okId, rawId = pcall(function() return desc[property] end)
local assetId = okId and tonumber(rawId) or nil
if assetId and assetId > 0 then ids = {tostring(math.floor(assetId))} end
end
local animations = {}
for _, child in ipairs(folder:GetChildren()) do
if child:IsA("Animation") then table.insert(animations, child) end
end
if #animations == 0 then
local animation = Instance.new("Animation")
animation.Name = folderName == "idle" and "Animation1" or (folderName == "walk" and "WalkAnim" or folderName .. "Anim")
animation.Parent = folder
animations[1] = animation
end
for index, animation in ipairs(animations) do
local id = ids[math.min(index, #ids)]
animation.AnimationId = "rbxassetid://" .. id
if folderName == "idle" and index == 1 then animation.Name = "Animation1" end
if folderName == "idle" and index == 2 then animation.Name = "Animation2" end
end
restored += 1
end
end
task.wait(0.1)
pcall(function() animate.Disabled = false end)
pcall(function() desc:Destroy() end)
if restored > 0 then
toast("Animations reset to default", "ok")
else
toast("Could not restore default animation IDs", "bad")
end
finishReset()
end)
end)
local function toggleBookmark(id, name)
local key = tostring(id)
local nowSaved
if savedBookmarks[key] then
savedBookmarks[key] = nil
nowSaved = false
else
local mt = metaCache[key] or {}
savedBookmarks[key] = {Id = id, Name = name, Fav = false, Time = tick(), Kind = mt.kind, Amount = mt.amount, Creator = mt.creator}
nowSaved = true
ensureMeta(id, true)
captureBundleData(id)
end
saveJSON(SAVED_BUNDLES_FILE, savedBookmarks)
for _, c in ipairs(cards) do
if c.key == key then c.refreshSave() end
end
if activeBundle and tostring(activeBundle.Id) == key then refreshActiveInfo() end
if currentTab == "Saved" and not nowSaved and refreshSavedView then refreshSavedView() end
toast(nowSaved and "Saved to your list" or "Removed from saved", nowSaved and "ok" or nil)
return nowSaved
end
starBtn.MouseButton1Click:Connect(function()
if activeBundle then toggleBookmark(activeBundle.Id, activeBundle.Name) end
end)
local function clearSkeletons()
for _, f in ipairs(skeletons) do stopShimmer(f); f:Destroy() end
table.clear(skeletons)
end
local function clearGrid()
for _, c in ipairs(cards) do
if c.skeleton then stopShimmer(c.skeleton) end
c.box:Destroy()
end
table.clear(cards)
end
local function showSkeletons()
clearGrid()
clearSkeletons()
gridMsg.Visible = false
for i = 1, PER_PAGE do
local f = new("Frame", {BackgroundColor3 = C.surface, BackgroundTransparency = 0.35, BorderSizePixel = 0, Size = UDim2.fromOffset(CW, CH), LayoutOrder = i}, gridHolder)
corner(f, s(12))
applyShimmer(f, C.panel, C.surfaceHi)
skeletons[#skeletons + 1] = f
end
end
local function showGridMessage(msg)
clearGrid()
clearSkeletons()
gridMsg.Text = msg
gridMsg.Visible = true
end
local function updatePager(total)
local pages = math.max(1, math.ceil(total / PER_PAGE))
local more = (currentTab == "Discover" and catalogCursor ~= nil and not catalogCursor.IsFinished) and true or false
pageLbl.Text = "Page " .. currentPageIndex .. " / " .. pages .. (more and "+" or "") .. " · " .. total .. (more and "+" or "") .. " results"
local canPrev = currentPageIndex > 1
local canNext = currentPageIndex < pages or more
setBtn(prevBtn, C.surfaceHi, canPrev and 0.3 or 0.7, canPrev and C.text or C.textMuted)
setBtn(nextBtn, C.surfaceHi, canNext and 0.3 or 0.7, canNext and C.text or C.textMuted)
end
local cardOrder = 0
local function buildCard(entry, idx)
local id = entry.Id
local key = tostring(id)
seedMeta(entry)
ensureMeta(id, false)
cardOrder += 1
local box = new("ViewportFrame", {
Name = "Card", BackgroundColor3 = C.panel, BackgroundTransparency = 0.25, BorderSizePixel = 0, ClipsDescendants = true,
Size = UDim2.fromOffset(CW, CH),
LayoutOrder = cardOrder,
Ambient = Color3.fromRGB(160, 160, 165), LightColor = WHITE, LightDirection = Vector3.new(-1, -1.5, -1),
}, gridHolder)
corner(box, s(12))
local boxStroke = stroke(box, C.stroke, 1, 0.5)
local boxScale = new("UIScale", {Scale = 0.86}, box)
tw(boxScale, 0.3 + idx * 0.05, {Scale = 1}, Enum.EasingStyle.Back)
local skeleton = new("Frame", {Size = UDim2.fromScale(1, 1), BackgroundColor3 = C.surface, BackgroundTransparency = 0.15, BorderSizePixel = 0, ZIndex = 2}, box)
corner(skeleton, s(12)); applyShimmer(skeleton, C.panel, C.surfaceHi)
local cpad = s(6)
new("TextLabel", {
Position = UDim2.fromOffset(cpad + s(2), CH - AH - cpad - s(22)), Size = UDim2.fromOffset(CW - cpad * 2 - s(4), s(20)), BackgroundTransparency = 1,
Text = entry.Name or "Unknown", Font = Enum.Font.GothamBold, TextSize = s(15), TextColor3 = C.text, TextXAlignment = Enum.TextXAlignment.Left,
TextTruncate = Enum.TextTruncate.AtEnd, ZIndex = 4,
}, box)
local badge = mkBadge(box, {Position = UDim2.fromOffset(cpad, cpad), ZIndex = 5})
bindBadge(badge, id)
local hit = new("TextButton", {Size = UDim2.new(1, 0, 1, -(AH + s(12))), BackgroundTransparency = 1, Text = "", ZIndex = 5}, box)
local cardActions = new("Frame", {Position = UDim2.fromOffset(cpad, CH - AH - cpad), Size = UDim2.fromOffset(CW - cpad * 2, AH), BackgroundTransparency = 1, ZIndex = 6}, box)
local saveBtn = mkBtn(cardActions, {Text = "+ Save", Size = UDim2.new(0.5, -s(3), 1, 0), TextSize = s(12), ZIndex = 6}, C.surfaceHi, 0.25, C.textDim)
corner(saveBtn, s(6))
local wearBtn = mkBtn(cardActions, {Text = "▶ Wear", Size = UDim2.new(0.5, -s(3), 1, 0), Position = UDim2.new(0.5, s(3), 0, 0), TextSize = s(12), ZIndex = 6}, C.surfaceHi, 0.15, C.text)
corner(wearBtn, s(6))
local function refreshSave()
local saved = savedBookmarks[key] ~= nil
saveBtn.Text = saved and "✓ Saved" or "+ Save"
saveBtn.TextColor3 = saved and C.gold or C.textDim
end
refreshSave()
if currentTab == "Saved" then
local favBtn = mkBtn(box, {Text = (entry.Fav and "★" or "☆"), Size = UDim2.fromOffset(s(28), s(28)), Position = UDim2.new(1, -s(32), 0, s(4)), TextSize = s(18), ZIndex = 7}, C.overlay, 0.4, entry.Fav and C.gold or C.textDim)
round(favBtn)
favBtn.MouseButton1Click:Connect(function()
local bm = savedBookmarks[key]
if bm then
bm.Fav = not bm.Fav
saveJSON(SAVED_BUNDLES_FILE, savedBookmarks)
favBtn.Text = bm.Fav and "★" or "☆"
favBtn.TextColor3 = bm.Fav and C.gold or C.textDim
end
end)
end
local record = {key = key, box = box, skeleton = skeleton, refreshSave = refreshSave}
record.restore = function()
local selected = activeBundle ~= nil and tostring(activeBundle.Id) == key
boxStroke.Color = selected and C.accent2 or C.stroke
boxStroke.Thickness = selected and 2 or 1
boxStroke.Transparency = selected and 0 or 0.5
end
cards[#cards + 1] = record
record.restore()
hit.MouseEnter:Connect(function()
tw(box, 0.12, {BackgroundColor3 = C.surface})
boxStroke.Color = C.accent2
boxStroke.Transparency = 0
end)
hit.MouseLeave:Connect(function()
tw(box, 0.15, {BackgroundColor3 = C.panel})
record.restore()
end)
hit.MouseButton1Click:Connect(function() inspectBundle(entry) end)
saveBtn.MouseButton1Click:Connect(function() toggleBookmark(id, entry.Name) end)
wearBtn.MouseButton1Click:Connect(function()
wearBtn.Text = "…"
wearBundle(id, entry.Name, function()
if wearBtn.Parent then wearBtn.Text = "▶ Wear" end
end)
end)
task.spawn(function()
task.wait(0.14 * idx)
if not box.Parent then return end
local function dropSkeleton()
if skeleton.Parent then
tw(skeleton, 0.25, {BackgroundTransparency = 1})
task.delay(0.3, function()
stopShimmer(skeleton)
skeleton:Destroy()
end)
end
end
local details = getDetails(id)
if not box.Parent then return end
if not details then dropSkeleton(); return end
local map = mapBundleItems(details.Items)
local usable = {}
for _, slot in ipairs(CARD_ORDER) do
if map[slot] and #usable < 4 then usable[#usable + 1] = {slot = slot, payload = map[slot]} end
end
if #usable == 0 then dropSkeleton(); return end
local rig, animator = buildRig(box)
if not rig or not box.Parent then dropSkeleton(); return end
local i, current = 1, nil
while box.Parent do
local u = usable[i]
local list = getTracksFor(u.payload, u.slot)
if #list > 0 then
if current then pcall(function() current:Stop(0.1) end); current = nil end
local ok, played = tryPlayTrack(animator, list[1], #list == 1)
if ok then
current = played
applySpeed(current, u.slot)
dropSkeleton()
end
end
task.wait(2.6)
i = i % #usable + 1
end
if current then pcall(function() current:Stop(0) end) end
end)
end
drawGridPage = function(dataList)
clearGrid()
clearSkeletons()
gridMsg.Visible = false
if not dataList or #dataList == 0 then
showGridMessage(currentTab == "Saved" and "No saved bundles match" or (currentTab == "Recent" and "No recently used bundles yet" or "No results"))
updatePager(0)
return
end
local start = (currentPageIndex - 1) * PER_PAGE + 1
local ending = math.min(currentPageIndex * PER_PAGE, #dataList)
if start > #dataList then
currentPageIndex = 1
start = 1
ending = math.min(PER_PAGE, #dataList)
end
local idx = 1
for i = start, ending do
local entry = dataList[i]
if not entry then break end
buildCard(entry, idx)
idx += 1
end
updatePager(#dataList)
end
refreshTags = function(q, spec)
for _, p in ipairs(tagPills) do p:Destroy() end
table.clear(tagPills)
local issueSet = {}
for _, issue in ipairs(spec.issues or {}) do issueSet[issue] = true end
local any = false
for _, tg in ipairs(q.tags) do
any = true
local txt = tg.key .. ":" .. tg.val
local bad = issueSet[txt] == true
local pillBtn = mkBtn(tagsRow, {
Text = txt, AutomaticSize = Enum.AutomaticSize.X, Size = UDim2.fromOffset(0, TAG_H),
TextSize = s(14), LayoutOrder = #tagPills + 1,
}, bad and C.badBg or C.pillBg, 0.25, bad and C.badTx or C.pillTx)
corner(pillBtn, math.floor(TAG_H / 2))
pad(pillBtn, s(15), s(15))
table.insert(tagPills, pillBtn)
pillBtn.MouseButton1Click:Connect(function()
local newTags = {}
for _, t in ipairs(q.tags) do
if not (t.key == tg.key and t.val == tg.val) then newTags[#newTags + 1] = t end
end
local parts = {}
if q.keyword ~= "" then parts[#parts + 1] = q.keyword end
for _, t in ipairs(newTags) do
local v = t.val
if v:find("%s") then v = '"' .. v .. '"' end
parts[#parts + 1] = t.key .. ":" .. v
end
local newText = table.concat(parts, " ")
sb.Text = newText
if runSearch then runSearch(newText) end
end)
end
tagHint.Visible = not any
for tabName, set in pairs(chipButtons) do
for tag, b in pairs(set) do
local shown = (tabName == currentTab)
b.Visible = shown
if shown then
local active = false
for _, t in ipairs(q.tags) do
if (t.key .. ":" .. t.val) == tag then active = true; break end
end
if active then setBtn(b, C.surfaceHi, 0.1, C.text)
else setBtn(b, C.surface, 0.35, C.textDim) end
end
end
end
end
local CHIP_SETS = {
Discover = {
{label = "Free", tag = "price:free"},
{label = "Newest", tag = "sort:newest"},
{label = "Popular", tag = "sort:mostfavorited"},
{label = "Cheapest", tag = "sort:pricelowtohigh"},
{label = "Featured", tag = "category:featured"},
{label = "Sale", tag = "sale:on"},
{label = "Best sellers", tag = "sort:bestselling"},
{label = "Priciest", tag = "sort:pricehightolow"},
{label = "Under 100", tag = "price:0-100"},
{label = "Under 500", tag = "price:0-500"},
{label = "100–500", tag = "price:100-500"},
{label = "Premium", tag = "category:premium"},
{label = "Collectibles", tag = "category:collectibles"},
{label = "Community", tag = "category:community"},
{label = "Recommended", tag = "category:recommended"},
{label = "This week", tag = "period:week"},
{label = "This month", tag = "period:month"},
},
Saved = {
{label = "Favorites", tag = "is:favorite"},
{label = "Newest", tag = "sort:newest"},
{label = "Oldest", tag = "sort:oldest"},
{label = "A–Z", tag = "sort:name"},
{label = "Free", tag = "is:free"},
{label = "Cheapest", tag = "sort:pricelowtohigh"},
{label = "Offsale", tag = "is:offsale"},
{label = "Paid", tag = "is:paid"},
{label = "Priciest", tag = "sort:pricehightolow"},
},
Presets = {
{label = "Newest", tag = "sort:newest"},
{label = "Oldest", tag = "sort:oldest"},
{label = "A–Z", tag = "sort:name"},
},
Recent = {
{label = "Newest", tag = "sort:newest"},
{label = "Oldest", tag = "sort:oldest"},
{label = "A–Z", tag = "sort:name"},
{label = "Free", tag = "is:free"},
{label = "Paid", tag = "is:paid"},
{label = "Cheapest", tag = "sort:pricelowtohigh"},
{label = "Priciest", tag = "sort:pricehightolow"},
{label = "Offsale", tag = "is:offsale"},
},
}
for tabName, set in pairs(CHIP_SETS) do
chipButtons[tabName] = {}
for i, c in ipairs(set) do
local b = mkBtn(chipsRow, {
Text = c.label, AutomaticSize = Enum.AutomaticSize.X, Size = UDim2.fromOffset(0, CHIP_H),
TextSize = s(13), LayoutOrder = i, Visible = (tabName == "Discover"),
}, C.surface, 0.35, C.textDim)
corner(b, math.floor(CHIP_H / 2))
pad(b, s(14), s(14))
chipButtons[tabName][c.tag] = b
b.MouseButton1Click:Connect(function()
local q = parseQuery(sb.Text)
local found = false
for j = #q.tags, 1, -1 do
local tg = q.tags[j]
if (tg.key .. ":" .. tg.val) == c.tag then table.remove(q.tags, j); found = true; break end
end
if not found then
local k, v = c.tag:match("^(%a+):(.+)$")
if k then
for j = #q.tags, 1, -1 do
if q.tags[j].key == k then table.remove(q.tags, j) end
end
q.tags[#q.tags + 1] = {key = k, val = v}
end
end
local newText = buildQuery(q)
sb.Text = newText
if runSearch then runSearch(newText) end
end)
end
end
local applySuggestionRef
local TAG_VALUES = {
sort = {"relevance", "newest", "mostfavorited", "bestselling", "price_low_to_high", "price_high_to_low"},
price_min = {"50", "100", "500", "1000"},
price_max = {"100", "300", "500", "1000"},
price = {"free", "0-100", "100-500"},
creator = {"Roblox"},
sale = {"on", "all"},
category = {"featured", "collectibles", "community", "premium", "recommended"},
sales = {"collectibles", "premium", "all"},
period = {"day", "week", "month", "alltime"},
limit = {"10", "28", "30", "60", "120"},
is = {"favorite", "free", "paid", "offsale"},
}
local SORT_SAVED_VALUES = {"newest", "oldest", "name", "price_low_to_high", "price_high_to_low", "favorites"}
local SORT_RECENT_VALUES = {"newest", "oldest", "name"}
local KEYS_DISCOVER = {"sort", "price", "price_min", "price_max", "creator", "sale", "category", "sales", "period", "limit"}
local KEYS_SAVED = {"sort", "price", "price_min", "price_max", "creator", "is"}
local KEYS_PRESETS = {"sort"}
local KEYS_RECENT = {"sort", "price", "price_min", "price_max", "creator", "is"}
local suggestStrip = new("Frame", {
Name = "Suggest", Position = UDim2.fromOffset(PAD, TOPH - s(34)), Size = UDim2.fromOffset(LW, s(30)),
BackgroundColor3 = C.overlay, BackgroundTransparency = 0.08, BorderSizePixel = 0, Visible = false, ZIndex = 60,
}, mf)
corner(suggestStrip, s(10)); stroke(suggestStrip, C.stroke, 1, 0.4)
local suggestRow = new("ScrollingFrame", {
Size = UDim2.fromScale(1, 1), BackgroundTransparency = 1, BorderSizePixel = 0, ScrollBarThickness = 0,
CanvasSize = UDim2.new(0, 0, 0, 0), AutomaticCanvasSize = Enum.AutomaticSize.X,
ScrollingDirection = Enum.ScrollingDirection.X, ZIndex = 61,
}, suggestStrip)
new("UIListLayout", {FillDirection = Enum.FillDirection.Horizontal, Padding = UDim.new(0, s(5)), SortOrder = Enum.SortOrder.LayoutOrder, VerticalAlignment = Enum.VerticalAlignment.Center}, suggestRow)
pad(suggestRow, s(5), s(5), 0, 0)
local currentSuggestions = {}
local function getSuggestions()
local text = sb.Text
local token = text:match("(%S*)$") or ""
local before = text:sub(1, #text - #token)
local lower = token:lower()
local out = {}
local key, valPrefix = lower:match("^([%a_]+):(.*)$")
local origKey = key
if key then key = KEY_ALIASES[key] or key end
if key and VALID_KEYS[key] then
local vals
if key == "sort" then
vals = (currentTab == "Discover" and TAG_VALUES.sort) or (currentTab == "Saved" and SORT_SAVED_VALUES) or SORT_RECENT_VALUES
elseif key == "is" and currentTab == "Recent" then
vals = {"free", "paid", "offsale"}
else
vals = TAG_VALUES[key]
end
local normPrefix = ((valPrefix or ""):gsub("_", ""))
for _, v in ipairs(vals or {}) do
local normVal = (v:lower():gsub("_", ""))
if normVal:find(normPrefix, 1, true) == 1 and normVal ~= normPrefix then
out[#out + 1] = {label = origKey .. ":" .. v, apply = before .. origKey .. ":" .. v .. " ", run = true}
end
end
return out
end
local used = {}
for _, tg in ipairs(parseQuery(text).tags) do used[tg.key] = true end
local keys = (currentTab == "Discover" and KEYS_DISCOVER) or (currentTab == "Saved" and KEYS_SAVED) or (currentTab == "Presets" and KEYS_PRESETS) or KEYS_RECENT
for _, k in ipairs(keys) do
local ak = KEY_ALIASES[k] or k
if not used[ak] and (lower == "" or k:find(lower, 1, true) == 1) and (k .. ":") ~= lower then
out[#out + 1] = {label = k .. ":", apply = before .. k .. ":", run = false}
end
end
if #lower >= 2 then
local seen, count = {}, 0
local function consider(name)
if count >= 4 or type(name) ~= "string" then return end
local ln = name:lower()
if seen[ln] or ln == lower or not ln:find(lower, 1, true) then return end
seen[ln] = true
count += 1
out[#out + 1] = {label = name, apply = before .. name .. " ", run = true}
end
if currentTab == "Presets" then
for _, v in ipairs(presetList) do consider(v.Name) end
else
for _, v in ipairs(historyList) do consider(v.Name) end
for _, v in pairs(savedBookmarks) do if type(v) == "table" then consider(v.Name) end end
for _, v in ipairs(searchResults) do consider(v.Name) end
end
end
while #out > 8 do table.remove(out) end
return out
end
local function refreshSuggestions()
for _, c in ipairs(suggestRow:GetChildren()) do
if c:IsA("GuiObject") then c:Destroy() end
end
if sb.Text == "" then suggestStrip.Visible = false; return end
currentSuggestions = getSuggestions()
if #currentSuggestions == 0 then suggestStrip.Visible = false; return end
new("TextLabel", {
Text = "Tab ⇥", LayoutOrder = 0, AutomaticSize = Enum.AutomaticSize.X, Size = UDim2.fromOffset(0, s(22)),
BackgroundTransparency = 1, Font = Enum.Font.GothamBold, TextSize = s(12), TextColor3 = C.textMuted, ZIndex = 62,
}, suggestRow)
for i, sg in ipairs(currentSuggestions) do
local b = mkBtn(suggestRow, {
Text = sg.label, AutomaticSize = Enum.AutomaticSize.X, Size = UDim2.fromOffset(0, s(22)),
TextSize = s(13), LayoutOrder = i, ZIndex = 62,
}, i == 1 and C.surfaceHi or C.surface, i == 1 and 0 or 0.2, i == 1 and C.text or C.textDim)
corner(b, s(11)); pad(b, s(10), s(10))
b.MouseButton1Down:Connect(function() applySuggestionRef(sg) end)
end
suggestStrip.Visible = true
end
applySuggestionRef = function(sg)
sb.Text = sg.apply
if sg.run and runSearch then runSearch(sg.apply) end
refreshSuggestions()
task.delay(0.05, function()
pcall(function()
sb:CaptureFocus()
sb.CursorPosition = #sb.Text + 1
end)
end)
end
sb:GetPropertyChangedSignal("Text"):Connect(refreshSuggestions)
track(uis.InputBegan:Connect(function(input)
if input.KeyCode == Enum.KeyCode.Tab and suggestStrip.Visible and currentSuggestions[1] then
applySuggestionRef(currentSuggestions[1])
end
end))
local renderPresets, presetPanel, presetEmpty, saveCurrentBtn, hideConfirm, confirmOverlay
do
local ROW_H = s(54)
local focusNameId
local pendingDelete
presetPanel = new("ScrollingFrame", {
Position = UDim2.fromOffset(0, GRID_Y), Size = UDim2.fromOffset(LW, GRID_H), BackgroundColor3 = C.panel, BackgroundTransparency = 0.55,
BorderSizePixel = 0, ScrollBarThickness = s(3), ScrollBarImageColor3 = C.surfaceHi, CanvasSize = UDim2.new(0, 0, 0, 0),
AutomaticCanvasSize = Enum.AutomaticSize.Y, ScrollingDirection = Enum.ScrollingDirection.Y, Visible = false, ClipsDescendants = true, ZIndex = 2,
}, left)
corner(presetPanel, s(12)); stroke(presetPanel, C.stroke, 1, 0.7)
new("UIListLayout", {Padding = UDim.new(0, s(6)), SortOrder = Enum.SortOrder.LayoutOrder, HorizontalAlignment = Enum.HorizontalAlignment.Center}, presetPanel)
pad(presetPanel, s(8), s(8), s(8), s(8))
presetEmpty = new("TextLabel", {
Position = UDim2.fromOffset(0, GRID_Y), Size = UDim2.fromOffset(LW, GRID_H), BackgroundTransparency = 1, Text = "",
Font = Enum.Font.GothamSemibold, TextSize = s(15), TextColor3 = C.textMuted, TextWrapped = true, Visible = false, ZIndex = 4,
}, left)
saveCurrentBtn = mkBtn(chipsRow, {
Text = "+ Save current", AutomaticSize = Enum.AutomaticSize.X, Size = UDim2.fromOffset(0, CHIP_H),
TextSize = s(13), LayoutOrder = 0, Visible = false,
}, C.surfaceHi, 0.15, C.text)
corner(saveCurrentBtn, math.floor(CHIP_H / 2)); pad(saveCurrentBtn, s(14), s(14))
confirmOverlay = new("TextButton", {
Size = UDim2.fromScale(1, 1), BackgroundColor3 = C.overlay, BackgroundTransparency = 0.35, BorderSizePixel = 0, Text = "",
AutoButtonColor = false, Visible = false, Active = true, ZIndex = 90,
}, mf)
local DW, DH = s(300), s(152)
local dlg = new("Frame", {
AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(0.5, 0.5), Size = UDim2.fromOffset(DW, DH),
BackgroundColor3 = C.panel, BorderSizePixel = 0, Active = true, ZIndex = 91,
}, confirmOverlay)
corner(dlg, s(14)); stroke(dlg, C.stroke, 1, 0.2)
local dlgScale = new("UIScale", {Scale = 1}, dlg)
new("TextLabel", {
Position = UDim2.fromOffset(s(16), s(14)), Size = UDim2.fromOffset(DW - s(32), s(22)), BackgroundTransparency = 1,
Text = "Delete preset?", Font = Enum.Font.GothamBlack, TextSize = s(18), TextColor3 = C.text, TextXAlignment = Enum.TextXAlignment.Left, ZIndex = 92,
}, dlg)
local confirmMsg = new("TextLabel", {
Position = UDim2.fromOffset(s(16), s(42)), Size = UDim2.fromOffset(DW - s(32), s(50)), BackgroundTransparency = 1,
Text = "", Font = Enum.Font.Gotham, TextSize = s(14), TextColor3 = C.textDim, TextWrapped = true,
TextXAlignment = Enum.TextXAlignment.Left, TextYAlignment = Enum.TextYAlignment.Top, ZIndex = 92,
}, dlg)
local btnW = math.floor((DW - s(32) - s(10)) / 2)
local cancelBtn = mkBtn(dlg, {Text = "Cancel", Size = UDim2.fromOffset(btnW, s(36)), Position = UDim2.fromOffset(s(16), DH - s(16) - s(36)), TextSize = s(14), ZIndex = 92}, C.surfaceHi, 0.1, C.text)
corner(cancelBtn, s(9))
local deleteBtn = mkBtn(dlg, {Text = "Delete", Size = UDim2.fromOffset(btnW, s(36)), Position = UDim2.fromOffset(s(16) + btnW + s(10), DH - s(16) - s(36)), TextSize = s(14), ZIndex = 92}, Color3.fromRGB(160, 84, 84), 0, WHITE)
corner(deleteBtn, s(9))
hideConfirm = function()
pendingDelete = nil
confirmOverlay.Visible = false
end
local function showConfirm(p)
pendingDelete = p
confirmMsg.Text = 'Do you want to delete this preset?\n"' .. tostring(p.Name) .. '"'
confirmOverlay.BackgroundTransparency = 1
confirmOverlay.Visible = true
dlgScale.Scale = 0.9
tw(confirmOverlay, 0.15, {BackgroundTransparency = 0.35})
tw(dlgScale, 0.22, {Scale = 1}, Enum.EasingStyle.Back)
end
cancelBtn.MouseButton1Click:Connect(hideConfirm)
confirmOverlay.MouseButton1Click:Connect(hideConfirm)
deleteBtn.MouseButton1Click:Connect(function()
local p = pendingDelete
hideConfirm()
if not p then return end
for i = #presetList, 1, -1 do
if presetList[i] == p then table.remove(presetList, i) end
end
saveJSON(PRESETS_FILE, presetList)
renderPresets(activeSpec or {})
toast("Preset deleted")
end)
local function snapshotEquipped()
local anims, any = {}, false
for slot, list in pairs(equippedAnims) do
if type(list) == "table" and #list > 0 then
local copy = {}
for _, a in ipairs(list) do copy[#copy + 1] = {Name = a.Name, AnimationId = a.AnimationId} end
anims[slot] = copy
any = true
end
end
return anims, any
end
local function nextPresetName()
local maxN = 0
for _, p in ipairs(presetList) do
local n = tonumber(string.match(p.Name or "", "^[Pp]reset (%d+)$"))
if n and n > maxN then maxN = n end
end
return "Preset " .. (maxN + 1)
end
local function summary(p)
local names, n = {}, 0
for _, slot in ipairs(buttonOrder) do
local l = p.Anims and p.Anims[slot]
if type(l) == "table" and #l > 0 then
n += 1
names[#names + 1] = shortNames[slot]
end
end
return n .. (n == 1 and " slot" or " slots") .. " · " .. table.concat(names, " · ")
end
local function inspectPreset(p)
local count = 0
for _ in pairs(p.Anims or {}) do count += 1 end
activeBundle = {Id = "preset:" .. tostring(p.Id), Name = p.Name, IsPreset = true, Count = count}
inspectGen = inspectGen + 1
masterGen = masterGen + 1
if masterTrack then pcall(function() masterTrack:Stop(0.1) end); masterTrack = nil end
table.clear(currentBundleItems)
hintLabel.Visible = false
for slot, list in pairs(p.Anims or {}) do
if type(list) == "table" and #list > 0 then
currentBundleItems[slot] = {Id = "preset:" .. tostring(p.Id) .. "/" .. slot, Name = p.Name, AssetType = slot, Direct = list}
end
end
refreshActiveInfo()
updateSelection()
local slot = currentBundleItems[selectedSlot] and selectedSlot or nil
if not slot then
for _, name in ipairs(buttonOrder) do
if currentBundleItems[name] then slot = name; break end
end
end
if not slot then
refreshSlotButtons()
setActionsEnabled(false)
setPreviewState("missing", selectedSlot)
return
end
selectedSlot = slot
refreshSlotButtons()
setActionsEnabled(true)
masterRig, masterAnimator = nil, nil
playMaster(slot)
end
local function buildRow(p, order)
local row = new("Frame", {
Size = UDim2.new(1, 0, 0, ROW_H), BackgroundColor3 = C.surface, BackgroundTransparency = 0.3, BorderSizePixel = 0, LayoutOrder = order,
}, presetPanel)
corner(row, s(10)); stroke(row, C.stroke, 1, 0.6)
local rowHit = new("TextButton", {Size = UDim2.fromScale(1, 1), BackgroundTransparency = 1, Text = "", AutoButtonColor = false, ZIndex = 2}, row)
local prevW, wearW, renW, delW, g4 = s(56), s(44), s(60), s(52), s(4)
local blockW = prevW + wearW + renW + delW + g4 * 3 + s(8)
local yb = math.floor((ROW_H - s(30)) / 2)
local nameBox = new("TextBox", {
Position = UDim2.fromOffset(s(8), s(6)), Size = UDim2.new(1, -(blockW + s(14)), 0, s(22)), BackgroundColor3 = C.overlay, BackgroundTransparency = 1,
BorderSizePixel = 0, Text = p.Name, ClearTextOnFocus = false, Font = Enum.Font.GothamBold, TextSize = s(15), TextColor3 = C.text,
TextXAlignment = Enum.TextXAlignment.Left, TextTruncate = Enum.TextTruncate.AtEnd, ClipsDescendants = true, ZIndex = 3,
}, row)
corner(nameBox, s(6)); pad(nameBox, s(4), s(4))
new("TextLabel", {
Position = UDim2.fromOffset(s(12), s(30)), Size = UDim2.new(1, -(blockW + s(20)), 0, s(16)), BackgroundTransparency = 1,
Text = summary(p), Font = Enum.Font.Gotham, TextSize = s(12), TextColor3 = C.textDim, TextXAlignment = Enum.TextXAlignment.Left,
TextTruncate = Enum.TextTruncate.AtEnd, ZIndex = 3,
}, row)
nameBox.Focused:Connect(function()
tw(nameBox, 0.12, {BackgroundTransparency = 0.25})
end)
nameBox.FocusLost:Connect(function()
tw(nameBox, 0.12, {BackgroundTransparency = 1})
local newName = nameBox.Text
newName = newName:gsub("^%s+", "")
newName = newName:gsub("%s+$", "")
newName = newName:sub(1, 24)
if newName == "" then
nameBox.Text = p.Name
elseif newName ~= p.Name then
p.Name = newName
nameBox.Text = newName
if activeBundle and activeBundle.IsPreset and activeBundle.Id == "preset:" .. tostring(p.Id) then
activeBundle.Name = newName
refreshActiveInfo()
end
saveJSON(PRESETS_FILE, presetList)
toast("Renamed to " .. newName, "ok")
else
nameBox.Text = newName
end
end)
local delBtn = mkBtn(row, {Text = "Delete", Size = UDim2.fromOffset(delW, s(30)), AnchorPoint = Vector2.new(1, 0), Position = UDim2.new(1, -s(8), 0, yb), TextSize = s(12), ZIndex = 3}, C.badBg, 0.1, C.badTx)
corner(delBtn, s(8))
local renBtn = mkBtn(row, {Text = "Rename", Size = UDim2.fromOffset(renW, s(30)), AnchorPoint = Vector2.new(1, 0), Position = UDim2.new(1, -(s(8) + delW + g4), 0, yb), TextSize = s(12), ZIndex = 3}, C.surfaceHi, 0.2, C.text)
corner(renBtn, s(8))
local wearBtn = mkBtn(row, {Text = "Wear", Size = UDim2.fromOffset(wearW, s(30)), AnchorPoint = Vector2.new(1, 0), Position = UDim2.new(1, -(s(8) + delW + renW + g4 * 2), 0, yb), TextSize = s(12), ZIndex = 3}, C.surfaceHi, 0.1, C.text)
corner(wearBtn, s(8))
local previewBtn = mkBtn(row, {Text = "Preview", Size = UDim2.fromOffset(prevW, s(30)), AnchorPoint = Vector2.new(1, 0), Position = UDim2.new(1, -(s(8) + delW + renW + wearW + g4 * 3), 0, yb), TextSize = s(12), ZIndex = 3}, C.surfaceHi, 0.1, C.text)
corner(previewBtn, s(8))
previewBtn.MouseButton1Click:Connect(function() inspectPreset(p) end)
rowHit.MouseButton1Click:Connect(function() inspectPreset(p) end)
renBtn.MouseButton1Click:Connect(function()
task.delay(0.03, function()
pcall(function()
nameBox:CaptureFocus()
nameBox.SelectionStart = 1
nameBox.CursorPosition = #nameBox.Text + 1
end)
end)
end)
delBtn.MouseButton1Click:Connect(function() showConfirm(p) end)
local busy = false
wearBtn.MouseButton1Click:Connect(function()
if busy then return end
if not lp.Character then toast("No character found", "bad"); return end
busy = true
wearBtn.Text = "…"
task.spawn(function()
local n = equipMap(lp.Character, p.Anims or {}, true)
toast(n > 0 and ("Equipped " .. p.Name) or "Couldn't equip that preset", n > 0 and "ok" or "bad")
if wearBtn.Parent then wearBtn.Text = "Wear" end
busy = false
end)
end)
if focusNameId == p.Id then
focusNameId = nil
task.delay(0.12, function()
pcall(function()
nameBox:CaptureFocus()
nameBox.SelectionStart = 1
nameBox.CursorPosition = #nameBox.Text + 1
end)
end)
end
end
renderPresets = function(spec)
spec = spec or {}
for _, c in ipairs(presetPanel:GetChildren()) do
if c:IsA("Frame") then c:Destroy() end
end
local kw = string.lower(spec.keyword or "")
local list = {}
for _, p in ipairs(presetList) do
if kw == "" or string.find(string.lower(p.Name or ""), kw, 1, true) then list[#list + 1] = p end
end
local mode = spec.sort or "new"
if mode == "name" then
table.sort(list, function(a, b) return string.lower(a.Name or "") < string.lower(b.Name or "") end)
elseif mode ~= "old" then
local rev = {}
for i = #list, 1, -1 do rev[#rev + 1] = list[i] end
list = rev
end
for i, p in ipairs(list) do buildRow(p, i) end
pageLbl.Text = #list .. (#list == 1 and " preset" or " presets")
if #presetList == 0 then
presetEmpty.Text = "No presets yet\nWear some animations, then tap\n“+ Save current”"
presetEmpty.Visible = true
elseif #list == 0 then
presetEmpty.Text = "No presets match"
presetEmpty.Visible = true
else
presetEmpty.Visible = false
end
end
saveCurrentBtn.MouseButton1Click:Connect(function()
local anims, any = snapshotEquipped()
if not any then toast("Nothing equipped yet. Wear an animation first", "bad"); return end
local p = {Id = tostring(os.time()) .. tostring(math.random(1000, 9999)), Name = nextPresetName(), Anims = anims, Time = os.time()}
presetList[#presetList + 1] = p
saveJSON(PRESETS_FILE, presetList)
focusNameId = p.Id
sb.Text = ""
if runSearch then runSearch("") end
toast("Saved as " .. p.Name .. ". Type to rename", "ok")
end)
end
refreshSavedView = function()
if currentTab ~= "Saved" then return end
if runSearch then runSearch(sb.Text) end
end
runSearch = function(text)
searchGen += 1
local myGen = searchGen
local q = parseQuery(text or "")
local spec = compileQuery(currentTab, q)
activeSpec = spec
currentPageIndex = 1
refreshTags(q, spec)
local isPresets = (currentTab == "Presets")
gridHolder.Visible = not isPresets
presetPanel.Visible = isPresets
saveCurrentBtn.Visible = isPresets
prevBtn.Visible = not isPresets
nextBtn.Visible = not isPresets
if isPresets then
clearGrid()
clearSkeletons()
gridMsg.Visible = false
renderPresets(spec)
return
end
presetEmpty.Visible = false
if currentTab ~= "Discover" then
local function buildAndDraw()
savedTabList = (currentTab == "Saved") and buildSavedList(spec) or buildHistoryList(spec)
drawGridPage(savedTabList)
end
local needPrice = spec.min ~= nil or spec.max ~= nil or spec.is == "free" or spec.is == "paid" or spec.is == "offsale" or spec.sort == "low" or spec.sort == "high"
if needPrice then
local src = {}
if currentTab == "Saved" then
for _, v in pairs(savedBookmarks) do if type(v) == "table" and v.Id then src[#src + 1] = v end end
else
for _, v in ipairs(historyList) do src[#src + 1] = v end
end
task.spawn(function()
resolveUnknownPrices(src)
if myGen ~= searchGen then return end
buildAndDraw()
end)
else
buildAndDraw()
end
return
end
searchResults = {}
catalogCursor = nil
showSkeletons()
pageLbl.Text = "Searching…"
task.spawn(function()
local params = buildParams(spec)
local ok, pages = pcall(function() return aes:SearchCatalog(params) end)
if myGen ~= searchGen then return end
if not ok or not pages then
showGridMessage("Search failed — try again")
pageLbl.Text = "Error"
return
end
catalogCursor = pages
local filtered = {}
local fetched = 0
local wantPrice = spec.min ~= nil or spec.max ~= nil
while true do
local page = catalogCursor:GetCurrentPage()
fetched += 1
if wantPrice then resolveUnknownPrices(page) end
local sawOverMax = false
for _, entry in ipairs(page) do
if passesClientFilter(entry, spec) then
filtered[#filtered + 1] = entry
elseif wantPrice and spec.max == 0 and spec.sort == nil then
local mt = seedMeta(entry)
if mt and mt.kind == "paid" and (mt.amount or 0) > 0 then sawOverMax = true end
end
end
if myGen ~= searchGen then return end
if #filtered > 0 or not wantPrice or sawOverMax or fetched >= 6 or catalogCursor.IsFinished then break end
local okAdv = pcall(function() catalogCursor:AdvanceToNextPageAsync() end)
if not okAdv or myGen ~= searchGen then break end
end
if myGen ~= searchGen then return end
searchResults = filtered
if #filtered == 0 then
showGridMessage("No bundles matched your search")
updatePager(0)
else
drawGridPage(searchResults)
end
end)
end
local function setTab(tab)
if currentTab == tab then return end
currentTab = tab
local order = {Discover = 0, Saved = 1, Recent = 2, Presets = 3}
local btns = {Discover = tabDiscover, Saved = tabSaved, Recent = tabRecent, Presets = tabPresets}
for name, b in pairs(btns) do
setBtn(b, C.surface, 1, name == tab and C.text or C.textMuted)
end
tw(tabIndicator, 0.2, {Position = UDim2.new(order[tab] / 4, s(order[tab] == 0 and 2 or 1), 0, s(2))})
runSearch(sb.Text)
refreshSuggestions()
end
tabDiscover.MouseButton1Click:Connect(function() setTab("Discover") end)
tabSaved.MouseButton1Click:Connect(function() setTab("Saved") end)
tabRecent.MouseButton1Click:Connect(function() setTab("Recent") end)
tabPresets.MouseButton1Click:Connect(function() setTab("Presets") end)
onHistoryChanged = function()
if currentTab == "Recent" and runSearch then runSearch(sb.Text) end
end
searchBtn.MouseButton1Click:Connect(function() runSearch(sb.Text) end)
sb.FocusLost:Connect(function(enter)
if not enter then return end
if suggestStrip.Visible and currentSuggestions[1] then
local cur = (sb.Text:gsub("%s+$", ""))
local lastToken = cur:match("(%S*)$") or ""
if lastToken:find(":", 1, true) then
local sg = currentSuggestions[1]
local ap = (sg.apply:gsub("%s+$", ""))
if ap ~= cur and ap:sub(1, #cur) == cur then
sb.Text = sg.apply
if sg.run then runSearch(sg.apply) end
return
end
end
end
runSearch(sb.Text)
end)
sb:GetPropertyChangedSignal("Text"):Connect(function()
clearBtn.Visible = sb.Text ~= ""
end)
clearBtn.MouseButton1Click:Connect(function()
sb.Text = ""
runSearch("")
end)
prevBtn.MouseButton1Click:Connect(function()
if pagingBusy or currentPageIndex <= 1 then return end
currentPageIndex -= 1
drawGridPage(currentTab ~= "Discover" and savedTabList or searchResults)
end)
nextBtn.MouseButton1Click:Connect(function()
if pagingBusy then return end
local list = currentTab ~= "Discover" and savedTabList or searchResults
local pages = math.max(1, math.ceil(#list / PER_PAGE))
local more = currentTab == "Discover" and catalogCursor ~= nil and not catalogCursor.IsFinished
if currentPageIndex < pages then
currentPageIndex += 1
drawGridPage(list)
elseif more then
pagingBusy = true
pageLbl.Text = "Loading…"
task.spawn(function()
local ok = pcall(function() catalogCursor:AdvanceToNextPageAsync() end)
pagingBusy = false
if not ok then toast("Couldn't load more results", "bad"); return end
local spec = activeSpec or {}
local before = #searchResults
local tries = 0
while true do
local page = catalogCursor:GetCurrentPage()
if spec.min ~= nil or spec.max ~= nil then resolveUnknownPrices(page) end
for _, e in ipairs(page) do
if passesClientFilter(e, spec) then searchResults[#searchResults + 1] = e end
end
tries += 1
if #searchResults > before or catalogCursor.IsFinished or tries >= 5 then break end
if not pcall(function() catalogCursor:AdvanceToNextPageAsync() end) then break end
end
if #searchResults == before then
toast("No more matching results", "bad")
pageLbl.Text = "Page " .. currentPageIndex
drawGridPage(searchResults)
return
end
currentPageIndex += 1
drawGridPage(searchResults)
end)
end
end)
local helpOpen = false
helpBtn.MouseButton1Click:Connect(function()
helpOpen = not helpOpen
settingsPanel.Visible = false
helpPanel.Visible = true
if helpOpen then
tw(helpPanel, 0.2, {BackgroundTransparency = 0.15})
tw(helpText, 0.22, {TextTransparency = 0})
setBtn(helpBtn, C.surfaceHi, 0.1, C.text)
setBtn(settingsBtn, C.surface, 0.2, C.textDim)
else
tw(helpPanel, 0.18, {BackgroundTransparency = 1})
tw(helpText, 0.18, {TextTransparency = 1})
task.delay(0.2, function()
if not helpOpen then helpPanel.Visible = false end
end)
setBtn(helpBtn, C.surface, 0.25, C.textDim)
end
end)
pad(helpScroll, s(14), s(14), s(10), s(10))
local settingsOpen = false
settingsBtn.MouseButton1Click:Connect(function()
settingsOpen = not settingsOpen
helpPanel.Visible = false
settingsPanel.Visible = true
if settingsOpen then
tw(settingsPanel, 0.2, {BackgroundTransparency = 0.15})
setBtn(settingsBtn, C.surfaceHi, 0.1, C.text)
setBtn(helpBtn, C.surface, 0.25, C.textDim)
helpOpen = false
else
tw(settingsPanel, 0.18, {BackgroundTransparency = 1})
task.delay(0.2, function()
if not settingsOpen then settingsPanel.Visible = false end
end)
setBtn(settingsBtn, C.surface, 0.2, C.textDim)
end
end)
local introTargets = nil
local function hideToPill()
introTargets = collectFade(mf)
for _, tg in ipairs(introTargets) do
local props
if tg.obj.BackgroundTransparency < 1 then props = {BackgroundTransparency = 1} end
if tg.txtT and tg.obj.TextTransparency < 1 then
props = props or {}
props.TextTransparency = 1
end
if props then tw(tg.obj, 0.2, props) end
end
task.delay(0.24, function()
mf.Visible = false
pill.Visible = true
pill.Size = UDim2.fromOffset(0, 0)
tw(pill, 0.22, {Size = UDim2.fromOffset(s(58), s(58))}, Enum.EasingStyle.Back)
end)
end
local function showFromPill()
pill.Visible = false
mf.Visible = true
local targets = introTargets or collectFade(mf)
for _, tg in ipairs(targets) do
if tg.obj.Parent then
local props
if tg.obj.BackgroundTransparency ~= tg.bgT then props = {BackgroundTransparency = tg.bgT} end
if tg.txtT and tg.obj.TextTransparency ~= tg.txtT then
props = props or {}
props.TextTransparency = tg.txtT
end
if props then tw(tg.obj, 0.28, props) end
end
end
end
closeBtn.MouseButton1Click:Connect(hideToPill)
minBtn.MouseButton1Click:Connect(hideToPill)
pill.MouseButton1Click:Connect(showFromPill)
local dragging, dragStart, startPos
top.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
dragging = true
dragStart = input.Position
startPos = mf.Position
end
end)
track(uis.InputChanged:Connect(function(input)
if not dragging then return end
if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then
local delta = input.Position - dragStart
mf.Position = UDim2.new(startPos.X.Scale, startPos.X.Offset + delta.X, startPos.Y.Scale, startPos.Y.Offset + delta.Y)
end
end))
track(uis.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
dragging = false
end
end))
track(uis.InputBegan:Connect(function(input, gameProcessed)
if gameProcessed then return end
if input.KeyCode == Enum.KeyCode.RightShift then
if mf.Visible then hideToPill() else showFromPill() end
elseif input.KeyCode == Enum.KeyCode.Escape and confirmOverlay.Visible then
hideConfirm()
elseif input.KeyCode == Enum.KeyCode.Escape and helpOpen then
helpOpen = false
helpPanel.Visible = false
helpText.TextTransparency = 1
helpPanel.BackgroundTransparency = 1
setBtn(helpBtn, C.surface, 0.25, C.textDim)
elseif input.KeyCode == Enum.KeyCode.Escape and settingsOpen then
settingsOpen = false
settingsPanel.Visible = false
settingsPanel.BackgroundTransparency = 1
setBtn(settingsBtn, C.surface, 0.2, C.textDim)
end
end))
env.UgcAnimStudioCleanup = function()
for _, c in ipairs(connections) do pcall(function() c:Disconnect() end) end
table.clear(connections)
for obj in pairs(spinning) do spinning[obj] = nil end
for obj, d in pairs(shimmers) do
pcall(function() d[2]:Cancel() end)
pcall(function() d[1]:Destroy() end)
shimmers[obj] = nil
end
if masterTrack then pcall(function() masterTrack:Stop(0) end) end
tweenShutdown()
pcall(function() g:Destroy() end)
end
runSearch("")
task.spawn(function()
task.wait(1)
if scriptSettings.restoreLastPack and lastPackData.Id then
wearBundle(lastPackData.Id, lastPackData.Name)
end
end)
env.UgcAnimStudio = {gui = g, main = mf}
end)
if not uiOk then
warn("[UGC Anim Studio] BUILD ERROR: " .. tostring(uiErr))
dbg("BUILD ERROR: " .. tostring(uiErr))
else
dbg("ui built successfully")
pcall(function()
local mf2 = env.UgcAnimStudio and env.UgcAnimStudio.main
if not mf2 then return end
local us = Instance.new("UIScale")
us.Scale = 0.86
us.Parent = mf2
local origPos = mf2.Position
mf2.Position = origPos + UDim2.fromOffset(0, 26)
local targets = collectFade(mf2)
for _, tg in ipairs(targets) do
if tg.bgT < 1 then tg.obj.BackgroundTransparency = 1 end
if tg.txtT and tg.txtT < 1 then tg.obj.TextTransparency = 1 end
end
tw(mf2, 0.55, {Position = origPos}, Enum.EasingStyle.Quint)
tw(us, 0.7, {Scale = 1}, Enum.EasingStyle.Back)
for _, tg in ipairs(targets) do
local props
if tg.bgT < 1 then props = {BackgroundTransparency = tg.bgT} end
if tg.txtT and tg.txtT < 1 then
props = props or {}
props.TextTransparency = tg.txtT
end
if props then tw(tg.obj, 0.55, props, Enum.EasingStyle.Quint) end
end
end)
endLoads code from the internet. It downloads more code when it runs (loadstring + HttpGet), which can change after it was posted. From: raw.githubusercontent.com.

Universal