Files
wnsrc/lua/pac3/editor/client/tools.lua

817 lines
24 KiB
Lua
Raw Normal View History

2024-08-04 22:55:00 +03:00
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local L = pace.LanguageString
pace.Tools = {}
function pace.AddToolsToMenu(menu)
menu.GetDeleteSelf = function() return false end
for key, data in pairs(pace.Tools) do
if #data.suboptions > 0 then
local menu = menu:AddSubMenu(L(data.name))
menu.GetDeleteSelf = function() return false end
for key, option in pairs(data.suboptions) do
menu:AddOption(option, function()
if pace.current_part:IsValid() then
data.callback(pace.current_part, key)
end
end)
end
else
menu:AddOption(L(data.name), function()
if pace.current_part:IsValid() then
data.callback(pace.current_part)
end
end)
end
end
end
function pace.AddTool(name, callback, ...)
for i,v in pairs(pace.Tools) do
if v.name == name then
table.remove(pace.Tools, i)
end
end
table.insert(pace.Tools, {name = name, callback = callback, suboptions = {...}})
end
pace.AddTool(L"convert legacy parts to new parts", function(part, suboption)
local class_translate = {
model = "model2",
material = "material_3d",
entity = "entity2",
bone = "bone3",
bone2 = "bone3",
light = "light2",
trail = "trail2",
clip = "clip2",
}
local model_prop_translate = {
Color = function(tbl, val) tbl.Color = Vector(val.r/255, val.g/255, val.b/255) end,
DoubleFace = function(tbl, val) tbl.NoCulling = val end,
Fullbright = function(tbl, val) tbl.NoLighting = val end,
TextureFilter = function(tbl, val) tbl.NoTextureFiltering = not val end,
LodOverride = function(tbl, val) tbl.LevelOfDetail = val end,
}
local registered_parts = pac.GetRegisteredParts()
local bones = {}
local material_translate = {}
for old_key in pairs(registered_parts.material.ShaderParams) do
local new_key = old_key:lower()
local new_val = registered_parts.material_3d[new_key]
local old_val = registered_parts.material[old_key]
if new_val ~= nil and type(new_val) == type(old_val) then
material_translate[old_key] = function(tbl, val)
tbl[new_key] = val
end
end
end
local prop_translate = {
model = model_prop_translate,
entity = table.Merge(model_prop_translate, {
HideEntity = function(tbl, val) tbl.NoDraw = val end
}),
material = material_translate,
bone = {
Bone = function(tbl, val)
if bones[tbl.UniqueID] and not bones[tbl.UniqueID][val] then
for k,v in pairs(bones[tbl.UniqueID]) do
if v.bone == 0 then
tbl.Bone = v.friendly
return
end
end
end
tbl.Bone = val
end,
}
}
local function calc_manip_offset(ent, bone, pos, ang)
if not ent:GetBoneMatrix(bone) then return end
local prev_pos = ent:GetManipulateBonePosition(bone)
local prev_ang = ent:GetManipulateBoneAngles(bone)
ent:ManipulateBonePosition(bone, Vector())
ent:ManipulateBoneAngles(bone, Angle())
pac.SetupBones(ent)
local pre = ent:GetBoneMatrix(bone)
ent:ManipulateBonePosition(bone, pos)
ent:ManipulateBoneAngles(bone, ang)
pac.SetupBones(ent)
local post = ent:GetBoneMatrix(bone)
ent:ManipulateBonePosition(bone, prev_pos)
ent:ManipulateBoneAngles(bone, prev_ang)
local m = pre:GetInverseTR() * post
return m:GetTranslation(), m:GetAngles()
end
local saved = {}
for _, part in pairs(pac.GetLocalParts()) do
if part.ClassName == "bone" or part.ClassName == "bone2" then
bones[part.UniqueID] = pac.GetAllBones(part:GetOwner())
if part:GetModelBoneIndex() and not part:GetAlternativeBones() then
local pos, ang = calc_manip_offset(part:GetOwner(), part:GetModelBoneIndex(), part:GetPosition(), part:GetAngles())
part:SetPosition(pos)
part:SetAngles(ang)
end
end
if not part:HasParent() then
table.insert(saved, part:ToTable())
end
end
pace.ClearParts()
local done = {}
local function walk(tbl, parent)
local new_classname = class_translate[tbl.self.ClassName]
if new_classname then
local old_classname = tbl.self.ClassName
tbl.self.ClassName = new_classname
if old_classname == "model" then
if tbl.self.BlurLength and tbl.self.BlurLength > 0 then
table.insert(tbl.children, {
self = {
ClassName = "motion_blur",
UniqueID = pac.Hash(),
BlurLength = tbl.self.BlurLength,
BlurSpacing = tbl.self.BlurSpacing,
},
children = {},
})
end
end
if old_classname == "entity" then
if tbl.self.Weapon == true and tbl.self.HideEntity then
table.insert(parent.children, {
self = {
ClassName = "weapon",
UniqueID = pac.Hash(),
NoDraw = true
},
children = {},
})
end
end
for key in pairs(registered_parts[old_classname].StorableVars) do
local value = tbl.self[key]
if value == nil then
value = registered_parts[old_classname][key]
end
if prop_translate[old_classname] and prop_translate[old_classname][key] then
tbl.self[key] = nil
prop_translate[old_classname][key](tbl.self, value)
if value ~= tbl.self[key] then
pac.Message("translated property: ", key, " = ", value, " to ", tbl.self[key])
end
elseif not registered_parts[new_classname].StorableVars[key] then
local msg = tbl.self.ClassName .. "." .. key
if not done[msg] then
pac.Message(Color(255,100,100), "cannot translate property ", msg)
done[msg] = true
end
end
end
end
if tbl.self.ClassName == "proxy" and tbl.self.VariableName == "Color" then
if isstring(tbl.self.Expression) and tbl.self.Expression ~= "" then
local r,g,b = unpack(tbl.self.Expression:Split(","))
r = tonumber(r)
g = tonumber(g)
b = tonumber(b)
if r and g and b then
tbl.self.Expression = (r/255)..","..(g/255)..","..(b/255)
end
end
end
if tbl.self.Model and tbl.self.Model:find("://", nil, true) and not tbl.self.Model:find(".zip", nil, true) then
tbl.self.ForceObjUrl = true
end
if tbl.children then
for _, child in ipairs(tbl.children) do
walk(child, tbl)
end
end
end
for _, tbl in ipairs(saved) do
walk(tbl)
end
for _, tbl in ipairs(saved) do
local part = pac.CreatePart(tbl.self.ClassName, pac.LocalPlayer, tbl)
end
end)
pace.AddTool(L"fix origin", function(part, suboption)
if not part.GetEntity then return end
local ent = part:GetOwner()
part:SetPositionOffset(-ent:OBBCenter() * part.Scale * part.Size)
end)
pace.AddTool(L"replace ogg with webaudio", function(part, suboption)
for _, part in pairs(pac.GetLocalParts()) do
if part.ClassName == "ogg" then
local parent = part:GetParent()
local audio = pac.CreatePart("webaudio")
audio:SetParent(parent)
audio:SetURL(part:GetURL())
audio:SetVolume(part:GetVolume())
audio:SetPitch(part:GetPitch())
audio:SetStopOnHide(not part:GetStopOnHide())
audio:SetPauseOnHide(part:GetPauseOnHide())
for k,v in ipairs(part:GetChildren()) do
v:SetParent(audio)
end
part:Remove()
end
end
end)
pace.AddTool(L"copy global id", function(obj)
SetClipboardText("\"" .. obj.UniqueID .. "\"")
end)
pace.AddTool(L"use legacy scale", function(part, suboption)
for _, part in pairs(pac.GetLocalParts()) do
if part.UseLegacyScale ~= nil then
part:SetUseLegacyScale(suboption == 1)
end
end
end, L"true", L"false")
pace.AddTool(L"scale this and children", function(part, suboption)
Derma_StringRequest(L"scale", L"input the scale multiplier (does not work well with bones)", "1", function(scale)
scale = tonumber(scale)
if scale and part:IsValid() then
local function scale_parts(part, scale)
if part.SetPosition then
part:SetPosition(part:GetPosition() * scale)
part:SetPositionOffset(part:GetPositionOffset() * scale)
end
if part.SetSize then
part:SetSize(part:GetSize() * scale)
end
for _, part in ipairs(part:GetChildren()) do
scale_parts(part, scale)
end
end
scale_parts(part, scale)
end
end)
end)
pace.AddTool(L"free children from part" ,function(part, suboption)
if part:IsValid() then
local children = part.Children
if #children == 0 then
Derma_Message(L"this part has no children...", L"free children from part", "ok")
return
end
Derma_Query(L"this process cannot be undone, are you sure?", L"free children from part", L"yes", function()
local grandparent = part:GetParent()
if grandparent == NULL then
grandparent = part:GetRootPart()
end
local parent = part
for _, child in ipairs(children) do
if child.BaseName ~= "base" and parent.BaseName ~= "base" then
child:SetAngles(child.Angles + parent.Angles)
child:SetPosition(child.Position + parent.Position)
child:SetAngleOffset(child.AngleOffset + parent.AngleOffset)
child:SetPositionOffset(child.PositionOffset + parent.PositionOffset)
end
child:SetParent(grandparent)
end
end, L"no", function() end)
end
end)
pace.AddTool(L"square model scales...", function(part, suboption)
Derma_StringRequest(L"model", L"input the model name that should get squared", "default.mdl", function(model)
for _, part in pairs(pac.GetLocalParts()) do
if part:IsValid() and part.GetModel then
local function square_scale(part)
if part.SetSize then
part:SetSize(part:GetSize() * part:GetSize())
end
if part.SetScale then
part:SetScale(part:GetScale() * part:GetScale())
end
end
if string.find(part:GetModel(),model) then
square_scale(part)
end
end
end
end)
end)
pace.AddTool(L"show only with active weapon", function(part, suboption)
local event = part:CreatePart("event")
local owner = part:GetRootPart():GetOwner()
if not owner.GetActiveWeapon or not owner:GetActiveWeapon():IsValid() then
owner = pac.LocalPlayer
end
local class_name = owner:GetActiveWeapon():GetClass()
event:SetEvent("weapon_class")
event:SetOperator("equal")
event:SetInvert(true)
event:SetRootOwner(true)
event:ParseArguments(class_name, suboption == 1)
end, L"hide weapon", L"show weapon")
pace.AddTool(L"import editor tool from file...", function()
local allowcslua = GetConVar("sv_allowcslua")
if allowcslua:GetBool() then
Derma_StringRequest(L"filename", L"relative to garrysmod/data/pac3_editor/tools/", "mytool.txt", function(toolfile)
if file.Exists("pac3_editor/tools/" .. toolfile,"DATA") then
local toolstr = file.Read("pac3_editor/tools/" .. toolfile,"DATA")
local ctoolstr = [[pace.AddTool(L"]] .. toolfile .. [[", function(part, suboption) ]] .. toolstr .. " end)"
RunStringEx(ctoolstr, "pac_editor_import_tool")
pac.LocalPlayer:ConCommand("pac_editor") --close and reopen editor
else
Derma_Message("File " .. "garrysmod/data/pac3_editor/tools/" .. toolfile .. " not found.","Error: File Not Found","OK")
end
end)
else
Derma_Message("Importing pac editor tools is disallowed on this server.","Error: Clientside Lua Disabled","OK")
end
end)
pace.AddTool(L"import editor tool from url...", function()
if GetConVar("sv_allowcslua"):GetBool() then
Derma_StringRequest(L"URL", L"URL to PAC Editor tool txt file", "http://www.example.com/tool.txt", function(toolurl)
local function ToolDLSuccess(body)
local toolname = pac.PrettifyName(toolurl:match(".+/(.-)%."))
local toolstr = body
local ctoolstr = [[pace.AddTool(L"]] .. toolname .. [[", function(part, suboption)]] .. toolstr .. " end)"
RunStringEx(ctoolstr, "pac_editor_import_tool")
pac.LocalPlayer:ConCommand("pac_editor") --close and reopen editor
end
pac.HTTPGet(toolurl,ToolDLSuccess,function(err)
pace.MessagePrompt(err, "HTTP Request Failed for " .. toolurl, "OK")
end)
end)
else
Derma_Message("Importing pac editor tools is disallowed on this server.","Error: Clientside Lua Disabled","OK")
end
end)
local function round_pretty(val)
return math.Round(val, 2)
end
pace.AddTool(L"round numbers", function(part)
local function ify_parts(part)
for _, key in pairs(part:GetStorableVars()) do
local val = part["Get" .. key](part)
if isnumber(val) then
part["Set" .. key](part, round_pretty(val))
elseif isvector(val) then
part["Set" .. key](part, Vector(round_pretty(val.x), round_pretty(val.y), round_pretty(val.z)))
elseif isangle(val) then
part["Set" .. key](part, Angle(round_pretty(val.p), round_pretty(val.y), round_pretty(val.r)))
end
end
for _, part in ipairs(part:GetChildren()) do
ify_parts(part)
end
end
ify_parts(part)
end)
do
local function fix_name(str)
str = str:lower()
str = str:gsub("_", " ")
return str
end
local hue =
{
"red",
"orange",
"yellow",
"green",
"turquoise",
"blue",
"purple",
"magenta",
}
local sat =
{
"pale",
"",
"strong",
}
local val =
{
"dark",
"",
"bright"
}
local function HSVToNames(h,s,v)
return
hue[math.Round(1 + (h / 360) * #hue)] or hue[1],
sat[math.ceil(s * #sat)] or sat[1],
val[math.ceil(v * #val)] or val[1]
end
local function ColorToNames(c)
return HSVToNames(ColorToHSV(Color(c.r, c.g, c.b)))
end
pace.AddTool(L"clear names", function(part, suboptions)
for k,v in pairs(pac.GetLocalParts()) do
v:SetName("")
end
pace.RefreshTree(true)
end)
end
pace.AddTool(L"Convert group of models to Expression 2 holograms", function(part)
local str_ref =
[[
I++, HN++, HT[HN,table] = table(I, Base, Base, 0, POSITION, ANGLES, SCALE, MODEL, MATERIAL, vec4(COLOR, ALPHA), SKIN)
]]
local str_header =
[[
@name [NAME]
#- Entity input directives
@inputs [Base]:entity
#- Spawning code directives
@persist [HT CT]:table [SpawnStatus CoreStatus]:string [HN CN I SpawnCounter]
@persist [ScaleFactor ToggleColMat ToggleShading] Indices
@persist [DefaultColor DefaultScale]:vector
#- Hologram index directives
@persist
if (first() | dupefinished()) {
Chip = entity(), Base = entity() #- Remove to use wired entity.
ScaleFactor = 1 #- Scale multiplier.
ToggleColMat = 1 #- Toggle for materials and colours.
ToggleShading = 0 #- Toggle for shading.
Indices = 1
#- Data structure
#- HN++, HT[HN, table] = table(Index, Local Entity (Entity:toWorld()), Parent Entity, ScaleType (Default 0), Pos, Ang, Scale, Model, Material, Color, Skin)
#- CN++, CT[CN, table] = table(Index, Clip Index, Pos, Ang)
#- Editing holograms
#- Scroll down to the bottom of the code to find where to insert your holo() code. In order to reference indexes
#- add a ", I_HologramName"" to the end of that holograms data line with "HologramName" being of your choosing.
#- Finally add this to a @persist directive eg "@persist [I_HologramName]", now you can address this in your holo() code.
#- For example, "holoBodygroup(I_HologramName, 2, 3)" which would be put in the "InitPostSpawn" section.
#- Advanced functionality
#- If you wish to take this system to the next level, you can. Instead of using multiple e2s for each "set" of holograms,
#- instead save each set of hologram data to a new file inside a folder of your liking. You can now use the #include "" directive
#- to bring that hologram data into a single e2 with this spawn code and compile multiple files into a single e2.
#- This has many benefits such as 1 interval instead of many, auto updating due to the chip pulling saved data and increased
#- organisation!
#- Your file hierarchy should look like this.
#- /expression2/
#- --> /yourfolder/
#- --> /hologram_data.txt & hologram_spawner.txt
# # # # # # # # # HOLOGRAM DATA START # # # # # # # # #
]]
local str_footer =
[[
# # # # # # # # # HOLOGRAM DATA END # # # # # # # # #
#- Create a hologram from data array
function table:holo() {
local Index = This[1, number] * Indices
if (This[2,entity]:isValid()) { Entity = This[2,entity] } else { Entity = holoEntity(This[2,number]) }
if (This[3,entity]:isValid()) { Parent = This[3,entity] } else { Parent = holoEntity(This[3,number]) }
local Rescale = (This[7, vector] / (This[4, number] ? 12 : 1)) * ScaleFactor
holoCreate(Index, Entity:toWorld(This[5, vector] * ScaleFactor), Rescale, Entity:toWorld(This[6, angle]), DefaultColor, This[8, string] ?: "cube")
holoParent(Index, Parent)
if (ToggleColMat) {
holoMaterial(Index, This[9, string])
holoColor(Index, This[10, vector4])
holoSkin(Index, This[11, number])
}
if (ToggleShading) { holoDisableShading(Index, 1) }
}
#- Clip a hologram from data array
function table:clip() {
holoClipEnabled(This[1, number] * Indices, This[2, number], 1)
holoClip(This[1, number] * Indices, This[2, number], This[3, vector] * ScaleFactor, This[4, vector], 0)
}
#- Load the contraption
function loadContraption() {
switch (SpawnStatus) {
case "InitSpawn",
if (clk("Start")) {
SpawnStatus = "LoadHolograms"
}
Chip:soundPlay("Blip", 0, "@^garrysmod/content_downloaded.wav", 0.212)
break
case "LoadHolograms",
while (perf() & holoCanCreate() & SpawnCounter < HN) {
SpawnCounter++
HT[SpawnCounter, table]:holo()
if (SpawnCounter >= HN) {
SpawnStatus = CN > 0 ? "LoadClips" : "PrintStatus"
SpawnCounter = 0
break
}
}
break
case "LoadClips",
while (perf() & SpawnCounter < CN) {
SpawnCounter++
CT[SpawnCounter, table]:clip()
if (SpawnCounter >= CN) {
SpawnStatus = "PrintStatus"
SpawnCounter = 0
break
}
}
break
case "PrintStatus",
printColor( vec(222,37,188), "PAC to Holo: ", vec(255,255,255), "Loaded " + HN + " holograms and " + CN + " clips." )
HT:clear()
CT:clear()
CoreStatus = "InitPostSpawn"
SpawnStatus = ""
break
}
}
CoreStatus = "InitSpawn"
SpawnStatus = "InitSpawn"
DefaultColor = vec(255, 255, 255)
runOnTick(1)
timer("Start", 500)
}
#- Credit to Shadowscion for the initial base hologram spawning code.
elseif (CoreStatus == "InitSpawn") {
loadContraption()
}
elseif (CoreStatus == "InitPostSpawn") {
#- This is your "if (first())" section of the code.
CoreStatus = "RunThisCode"
}
elseif (CoreStatus == "RunThisCode") {
#- This is your "interval()" ran section of the code.
runOnTick(0)
}]]
local function tovec(vec) return ("%s, %s, %s"):format(math.Round(vec.x, 4), math.Round(vec.y, 4), math.Round(vec.z, 4)) end
local function toang(vec) return ("%s, %s, %s"):format(math.Round(vec.p, 4), math.Round(vec.y, 4), math.Round(vec.r, 4)) end
local function part_to_holo(part)
local str_holo = str_ref
for CI, clip in ipairs(part:GetChildren()) do
if not clip:IsHidden() then
if clip.ClassName == "clip" then
local pos, ang = clip.Position, clip:CalcAngles(clip.Angles)
local normal = ang:Forward()
str_holo = str_holo .. " CN++, CT[CN,table] = table(I, " .. CI .. ", vec(" .. tovec(pos + normal) .. "), vec(" .. tovec(normal) .. "))\n"
elseif clip.ClassName == "clip2" then
local pos, ang = clip.Position, clip:CalcAngles(clip.Angles)
local normal = ang:Forward()
str_holo = str_holo .. " CN++, CT[CN,table] = table(I, " .. CI .. ", vec(" .. tovec(pos) .. "), vec(" .. tovec(normal) .. "))\n"
end
end
end
local scale = part:GetSize() * part:GetScale()
local pos, ang = part.Position, part.Angles
if not part.PositionOffset:IsZero() or not part.AngleOffset:IsZero() then
pos, ang = LocalToWorld(part.PositionOffset, part.AngleOffset, pos, ang)
end
local holo = str_holo:gsub("[A-Z]+",{
ALPHA = math.Round(part:GetAlpha() * 255, 4),
COLOR = tovec((part.ProperColorRange and part:GetColor()*255) or part:GetColor()),
SCALE = "vec(" .. tovec(Vector(scale.x, scale.y, scale.z)) .. ")",
ANGLES = "ang(" .. toang(ang) .. ")",
POSITION = "vec(" .. tovec(pos) .. ")",
MATERIAL = ("%q"):format(part:GetMaterial()),
MODEL = ("%q"):format(part:GetModel()),
SKIN = part.GetSkin and part:GetSkin() or "0",
PARENT = "entity()"
})
return holo
end
local function convert(part)
local out = {string.Replace(str_header, "[NAME]", part:GetName() or "savedpacholos")}
local completed = {}
local function recursiveConvert(parent)
if completed[parent] then return end
completed[parent] = true
for key, part in ipairs(parent:GetChildren()) do
if part.is_model_part and not part:IsHidden() then
out[#out + 1] = part_to_holo(part)
recursiveConvert(part)
end
end
end
recursiveConvert(part)
out[#out + 1] = str_footer
pac.LocalPlayer:ChatPrint("PAC --> Code saved in your Expression 2 folder under [expression2/pac/" .. part:GetName() .. ".txt" .. "].")
return table.concat(out)
end
file.CreateDir("expression2/pac")
file.Write("expression2/pac/" .. part:GetName() .. ".txt", convert(part))
end)
pace.AddTool(L"record surrounding props to pac", function(part)
local base = pac.CreatePart("group")
base:SetName("recorded props")
local origin = base:CreatePart("model")
origin:SetName("origin")
origin:SetBone("none")
origin:SetModel("models/dav0r/hoverball.mdl")
for key, ent in pairs(ents.FindInSphere(pac.EyePos, 1000)) do
if
not ent:IsPlayer() and
not ent:IsNPC() and
not ent:GetOwner():IsPlayer()
then
local mdl = origin:CreatePart("model")
mdl:SetModel(ent:GetModel())
local lpos, lang = WorldToLocal(ent:GetPos(), ent:GetAngles(), pac.EyePos, pac.EyeAng)
mdl:SetMaterial(ent:GetMaterial())
mdl:SetPosition(lpos)
mdl:SetAngles(lang)
local c = ent:GetColor()
mdl:SetColor(Vector(c.r,c.g,c.b))
mdl:SetAlpha(c.a / 255)
mdl:SetName(ent:GetModel():match(".+/(.-)%.mdl"))
end
end
end)
pace.AddTool(L"populate with bones", function(part,suboption)
local ent = part:GetOwner()
local bones = pac.GetModelBones(ent)
for bone,tbl in pairs(bones) do
if not tbl.is_special then
local child = pac.CreatePart("bone3")
child:SetParent(part)
child:SetBone(bone)
end
end
pace.RefreshTree(true)
end)
pace.AddTool(L"populate with dummy bones", function(part,suboption)
local ent = part:GetOwner()
local bones = pac.GetModelBones(ent)
for bone,tbl in pairs(bones) do
if not tbl.is_special then
local child = pac.CreatePart("model")
child:SetParent(part)
child:SetName(bone .. "_dummy")
child:SetBone(bone)
child:SetScale(Vector(0,0,0))
end
end
pace.RefreshTree(true)
end)
pace.AddTool(L"print part info", function(part)
PrintTable(part:ToTable())
end)
pace.AddTool(L"dump player submaterials", function()
local ply = pac.LocalPlayer
for id,mat in pairs(ply:GetMaterials()) do
chat.AddText(("%d %s"):format(id,tostring(mat)))
end
end)
pace.AddTool(L"stop all custom animations", function()
pac.animations.StopAllEntityAnimations(pac.LocalPlayer)
pac.animations.ResetEntityBoneMatrix(pac.LocalPlayer)
end)
pace.AddTool(L"copy from faceposer tool", function(part, suboption)
local group = pac.CreatePart("group")
local ent = pac.LocalPlayer
for i = 0, ent:GetFlexNum() - 1 do
local name = ent:GetFlexName(i)
local fp_flex = GetConVar("faceposer_flex" .. i):GetFloat()
local fp_scale = GetConVar("faceposer_scale"):GetFloat()
local weight = fp_flex * fp_scale
pac.Message(name, weight)
if weight ~= 0 then
local flex = group:CreatePart("flex")
flex:SetFlex(name)
flex:SetWeight(weight)
end
end
end)