Hey! I’m a terrible scripter. The extent of my scripting knowledge is basically a GUI close button. ANYWAY, there’s this amazing open source resource by 0_1195
It’s an avatar editor, really well made. Basically a godsend for the unexperienced such as myself. I do have one thing I’m trying to fix up! I’m having trouble altering the scripts to accept decals as faces, as some of the faces in my game are just decals and not roblox faces themselves. I think that the scripts search for an item class based on the ID (i.e, this is a hat, weld to head, etc.) I’ve been looking through but its just a mess to me (probably much more legible to actual scripters) and I don’t know where to start.
If some of the more experienced scripters could help me out with this, it’d be much appreciated!
To be clear, I need to find a way to allow decals in the same class as faces, so they are put on the player’s face when equipped. I’ll post (what I think are) the relevant scripts below. If you’d like to look in the actual full resource, the link above has a downloadable baseplate game to see all the components.
ModuleScript in ServerScriptService
local Http = game:GetService("HttpService")
local subcategory = 9
local url = "https://search.roblox.com/catalog/json?Subcategory=" .. subcategory .. "&IncludeNotForSale=true&ResultsPerPage=60&PageNumber="
local pages = 25
local assets = {}
for i = 1, pages do
local urlPage = url .. i
local data = Http:JSONEncode(Http:GetAsync(urlPage))
data = Http:JSONDecode(data)
for ii = 1, #data do
local asset = data[ii]
local id = asset.AssetId
local name = asset.Name
assets[#assets + 1] = "{Id = " ..id .. ", Name = \"" .. name .. "\"}"
end
print(i)
end
local source = "return { \n\t" .. table.concat(assets, ",\n\t") .. "\n}"
local module = Instance.new("ModuleScript")
module.Source = source
module.Parent = workspace
Script in ServerScriptService
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Marketplace = game:GetService("MarketplaceService")
local Insert = game:GetService("InsertService")
local Debris = game:GetService("Debris")
local Players = game:GetService("Players")
local remoteEvent = ReplicatedStorage.UpdateAvatar
local defaultBodyPartsR15 = script.HumanoidDefaultBodyPartsR15
local ACCESSORY_CAP = 10
local DEFAULT_SHIRT = "rbxassetid://855777285"
local DEFAULT_PANTS = "rbxassetid://867826313"
local DEFAULT_FACE = "rbxasset://textures/face.png"
local bannedAssets = {
-- ["1527622"] = true,
-- ["4640898"] = true,
-- ["1055299"] = true,
}
local allowedAssets = {
["8"] = true, -- Hat
["11"] = true, -- Shirt
["12"] = true, -- Pants
["17"] = true, -- Head
["18"] = true, -- Face
["27"] = true, -- Torso
["28"] = true, -- RightArm
["29"] = true, -- LeftArm
["30"] = true, -- LeftLeg
["31"] = true, -- RightLeg
["41"] = true, -- HairAccessory
["42"] = true, -- FaceAccessory
["43"] = true, -- NeckAccessory
["44"] = true, -- ShoulderAccessory
["45"] = true, -- FrontAccessory
["46"] = true, -- BackAccessory
["47"] = true, -- WaistAccessory
["48"] = true, -- ClimbAnimation
["50"] = true, -- FallAnimation
["51"] = true, -- IdleAnimation
["52"] = true, -- JumpAnimation
["53"] = true, -- RunAnimation
["54"] = true, -- SwimAnimation
["55"] = true, -- WalkAnimation
}
local playerWearingAssets = {}
local AssetTypeEnumLookup = {}
local function insertToTable(assets, productInfo)
table.insert(assets, {
id = productInfo.AssetId,
assetType = {
name = AssetTypeEnumLookup[productInfo.AssetTypeId].Name,
id = productInfo.AssetTypeId,
},
name = productInfo.Name
})
end
local function getOriginalHeadMesh()
local mesh = Instance.new("SpecialMesh")
mesh.MeshType = Enum.MeshType.Head
mesh.Scale = Vector3.new(1.25, 1.25, 1.25)
local originalSize = Instance.new("Vector3Value")
originalSize.Name = "OriginalSize"
originalSize.Value = Vector3.new(1.25, 1.25, 1.25)
originalSize.Parent = mesh
return mesh
end
local function getOriginalFace()
local decal = Instance.new("Decal")
decal.Texture = DEFAULT_FACE
return decal
end
local function getOriginalClothing(clothe)
if clothe == "Shirt" then
local shirt = Instance.new("Shirt")
shirt.ShirtTemplate = DEFAULT_SHIRT
return shirt
elseif clothe == "Pants" then
local pants = Instance.new("Pants")
pants.PantsTemplate = DEFAULT_PANTS
return pants
end
end
local function wear(player, id)
if id == nil or not (typeof(id) == "number") then
return
end
if not playerWearingAssets[player] then
return
end
if bannedAssets[tostring(id)] then
return
end
local productInfo, items
local success, err = pcall(function()
productInfo = Marketplace:GetProductInfo(id)
end)
if not success then
return warn(err)
end
local assetTypeId = tostring(productInfo.AssetTypeId)
if not allowedAssets[assetTypeId] then
return
end
local character = player.Character
local humanoid = character and character:FindFirstChildWhichIsA("Humanoid")
if humanoid == nil or humanoid.Health <= 0 then
return
end
local success, err = pcall(function()
items = Insert:LoadAsset(productInfo.AssetId)
end)
if not success then
return warn(err)
end
for i, descendant in ipairs(items:GetDescendants()) do
if descendant:IsA("LuaSourceContainer") or descendant:IsA("BackpackItem") then
Debris:AddItem(descendant, 0)
end
end
local characterAppearanceInfo = playerWearingAssets[player]
local index = nil
for i, asset in ipairs(characterAppearanceInfo.assets) do
if asset.id == id then
index = i
break
end
end
-- is there a better way to do this?
if assetTypeId == "8" or assetTypeId == "41" or assetTypeId == "42" or assetTypeId == "43" or assetTypeId == "44" or assetTypeId == "45" or assetTypeId == "46" or assetTypeId == "47" then
-- accessory
local accessory = items:GetChildren()[1]
if not accessory:IsA("Accoutrement") then
return
end
if index then
local wearing = character:FindFirstChild(accessory.Name)
if wearing then
Debris:AddItem(wearing, 0)
table.remove(characterAppearanceInfo.assets, index)
end
elseif #humanoid:GetAccessories() < ACCESSORY_CAP then
humanoid:AddAccessory(accessory)
insertToTable(characterAppearanceInfo.assets, productInfo)
end
elseif assetTypeId == "11" or assetTypeId == "12" then
-- clothing
local clothing = items:GetChildren()[1]
if not clothing:IsA("Clothing") then
return
end
local wearing = character:FindFirstChildWhichIsA(clothing.ClassName)
if wearing then
Debris:AddItem(wearing, 0)
end
if index then
table.remove(characterAppearanceInfo.assets, index)
getOriginalClothing(clothing.ClassName).Parent = character
else
for i, asset in ipairs(characterAppearanceInfo.assets) do
if productInfo.AssetTypeId == asset.assetType.id then
table.remove(characterAppearanceInfo.assets, i)
end
end
insertToTable(characterAppearanceInfo.assets, productInfo)
clothing.Parent = character
end
elseif assetTypeId == "48" or assetTypeId == "50" or assetTypeId == "51" or assetTypeId == "52" or assetTypeId == "53" or assetTypeId == "54" or assetTypeId == "55" then
-- animation
local animationFolder = items:GetChildren()[1]
local animateScript = character:FindFirstChild("Animate")
if index then
table.remove(characterAppearanceInfo.assets, index)
for i, value in ipairs(animationFolder:GetChildren()) do
if animateScript:FindFirstChild(value.Name) then
Debris:AddItem(animateScript[value.Name], 0)
end
end
else
for i, asset in ipairs(characterAppearanceInfo.assets) do
if productInfo.AssetTypeId == asset.assetType.id then
table.remove(characterAppearanceInfo.assets, i)
end
end
insertToTable(characterAppearanceInfo.assets, productInfo)
if animateScript then
for i, value in ipairs(animationFolder:GetChildren()) do
if animateScript:FindFirstChild(value.Name) then
Debris:AddItem(animateScript[value.Name], 0)
end
value.Parent = animateScript
end
end
end
elseif assetTypeId == "18" then
-- face
local decal = items:GetChildren()[1]
if not decal:IsA("Decal") then
return
end
local head = character:FindFirstChild("Head")
local wearing = head and head:FindFirstChildWhichIsA("Decal")
if wearing then
Debris:AddItem(wearing, 0)
end
if index then
table.remove(characterAppearanceInfo.assets, index)
player:LoadCharacterAppearance(getOriginalFace())
else
for i, asset in ipairs(characterAppearanceInfo.assets) do
if productInfo.AssetTypeId == asset.assetType.id then
table.remove(characterAppearanceInfo.assets, i)
end
end
insertToTable(characterAppearanceInfo.assets, productInfo)
decal.Parent = head
end
elseif assetTypeId == "17" then
-- head
local mesh = items:GetChildren()[1]
if not (mesh:IsA("SpecialMesh") or mesh:IsA("CylinderMesh")) then
return
end
local head = character:FindFirstChild("Head")
local wearing = head and head:FindFirstChildWhichIsA("SpecialMesh")
if wearing then
Debris:AddItem(wearing, 0)
end
if index then
table.remove(characterAppearanceInfo.assets, index)
player:LoadCharacterAppearance(getOriginalHeadMesh())
else
for i, asset in ipairs(characterAppearanceInfo.assets) do
if productInfo.AssetTypeId == asset.assetType.id then
table.remove(characterAppearanceInfo.assets, i)
end
end
insertToTable(characterAppearanceInfo.assets, productInfo)
player:LoadCharacterAppearance(mesh)
end
elseif assetTypeId == "27" or assetTypeId == "28" or assetTypeId == "29" or assetTypeId == "30" or assetTypeId == "31" then
-- body parts
local bodyPartFolder = items:FindFirstChild("R15ArtistIntent")
if index then
table.remove(characterAppearanceInfo.assets, index)
for i, part in ipairs(bodyPartFolder:GetChildren()) do
local partInCharacter = character:FindFirstChild(part.Name)
local bodyPartR15 = partInCharacter and humanoid:GetBodyPartR15(partInCharacter)
local defaultBodyPart = bodyPartR15 and defaultBodyPartsR15:FindFirstChild(part.Name)
if defaultBodyPart then
defaultBodyPart = defaultBodyPart:Clone()
humanoid:ReplaceBodyPartR15(bodyPartR15, defaultBodyPart)
end
end
else
for i, asset in ipairs(characterAppearanceInfo.assets) do
if productInfo.AssetTypeId == asset.assetType.id then
table.remove(characterAppearanceInfo.assets, i)
end
end
insertToTable(characterAppearanceInfo.assets, productInfo)
for i, part in ipairs(bodyPartFolder:GetChildren()) do
local partInCharacter = character:FindFirstChild(part.Name)
local bodyPartR15 = partInCharacter and humanoid:GetBodyPartR15(partInCharacter)
if bodyPartR15 then
humanoid:ReplaceBodyPartR15(bodyPartR15, part)
end
end
end
end
humanoid:BuildRigFromAttachments()
Debris:AddItem(items, 0)
remoteEvent:FireClient(player, characterAppearanceInfo)
end
local function skinTone(player, color)
if color == nil or not (typeof(color) == "string") then
return
end
if not playerWearingAssets[player] then
return
end
local character = player.Character
local humanoid = character and character:FindFirstChildWhichIsA("Humanoid")
if humanoid == nil or humanoid.Health <= 0 then
return
end
local characterAppearanceInfo = playerWearingAssets[player]
local brickColor = BrickColor.new(color)
characterAppearanceInfo.bodyColors.headColorId = brickColor.Number
characterAppearanceInfo.bodyColors.leftArmColorId = brickColor.Number
characterAppearanceInfo.bodyColors.leftLegColorId = brickColor.Number
characterAppearanceInfo.bodyColors.rightArmColorId = brickColor.Number
characterAppearanceInfo.bodyColors.rightLegColorId = brickColor.Number
characterAppearanceInfo.bodyColors.torsoColorId = brickColor.Number
local bodyColors = character:FindFirstChildWhichIsA("BodyColors")
bodyColors.HeadColor3 = brickColor.Color
bodyColors.LeftArmColor3 = brickColor.Color
bodyColors.LeftLegColor3 = brickColor.Color
bodyColors.RightArmColor3 = brickColor.Color
bodyColors.RightLegColor3 = brickColor.Color
bodyColors.TorsoColor3 = brickColor.Color
end
local function scale(player, name, value)
if name == nil or not (typeof(name) == "string") then
return
end
if value == nil or not (typeof(value) == "number") then
return
end
if not playerWearingAssets[player] then
return
end
local character = player.Character
local humanoid = character and character:FindFirstChildWhichIsA("Humanoid")
if humanoid == nil or humanoid.Health <= 0 then
return
end
if name == "BodyHeightScale" then
math.clamp(value, 95, 105)
elseif name == "BodyWidthScale" then
math.clamp(value, 70, 100)
elseif name == "HeadScale" then
math.clamp(value, 95, 100)
elseif name == "BodyProportionScale" then
math.clamp(value, 0, 100)
elseif name == "BodyTypeScale" then
math.clamp(value, 0, 100)
else
return
end
local scaleValue = humanoid and humanoid:FindFirstChild(name)
if not scaleValue then
return
end
scaleValue.Value = value/100
end
-- limit how often the client can fire
local function onServerEvent(player, action, ...)
if action == "wear" then
wear(player, ...)
elseif action == "skintone" then
skinTone(player, ...)
elseif action == "scale" then
scale(player, ...)
end
end
-- theres gotta be a better way
local function getHumanoidDescriptionFromCharacterAppearance(characterAppearanceInfo)
local humanoidDescription = Instance.new("HumanoidDescription")
humanoidDescription.BodyTypeScale = characterAppearanceInfo.scales.bodyType
humanoidDescription.DepthScale = characterAppearanceInfo.scales.depth
humanoidDescription.HeadScale = characterAppearanceInfo.scales.head
humanoidDescription.HeightScale = characterAppearanceInfo.scales.height
humanoidDescription.ProportionScale = characterAppearanceInfo.scales.proportion
humanoidDescription.WidthScale = characterAppearanceInfo.scales.width
humanoidDescription.HeadColor = BrickColor.new(characterAppearanceInfo.bodyColors.headColorId).Color
humanoidDescription.LeftArmColor = BrickColor.new(characterAppearanceInfo.bodyColors.leftArmColorId).Color
humanoidDescription.LeftLegColor = BrickColor.new(characterAppearanceInfo.bodyColors.leftLegColorId).Color
humanoidDescription.RightArmColor = BrickColor.new(characterAppearanceInfo.bodyColors.rightArmColorId).Color
humanoidDescription.RightLegColor = BrickColor.new(characterAppearanceInfo.bodyColors.rightLegColorId).Color
humanoidDescription.TorsoColor = BrickColor.new(characterAppearanceInfo.bodyColors.torsoColorId).Color
for i, asset in ipairs(characterAppearanceInfo.assets) do
if asset.assetType.name == "Hat" then
humanoidDescription.HatAccessory = humanoidDescription.HatAccessory .. "," .. asset.id
elseif asset.assetType.name == "BackAccessory" or asset.assetType.name == "Back Accessory" then
humanoidDescription.BackAccessory = humanoidDescription.BackAccessory .. "," .. asset.id
elseif asset.assetType.name == "FaceAccessory" or asset.assetType.name == "Face Accessory" then
humanoidDescription.FaceAccessory = humanoidDescription.FaceAccessory .. "," .. asset.id
elseif asset.assetType.name == "FrontAccessory" or asset.assetType.name == "Front Accessory" then
humanoidDescription.FrontAccessory = humanoidDescription.FrontAccessory .. "," .. asset.id
elseif asset.assetType.name == "HairAccessory" or asset.assetType.name == "Hair Accessory" then
humanoidDescription.HairAccessory = humanoidDescription.HairAccessory .. "," .. asset.id
elseif asset.assetType.name == "NeckAccessory" or asset.assetType.name == "Neck Accessory" then
humanoidDescription.NeckAccessory = humanoidDescription.NeckAccessory .. "," .. asset.id
elseif asset.assetType.name == "ShoulderAccessory" or asset.assetType.name == "Shoulder Accessory" then
humanoidDescription.ShouldersAccessory = humanoidDescription.ShouldersAccessory .. "," .. asset.id
elseif asset.assetType.name == "WaistAccessory" or asset.assetType.name == "Waist Accessory" then
humanoidDescription.WaistAccessory = humanoidDescription.WaistAccessory .. "," .. asset.id
elseif asset.assetType.name == "Face" then
humanoidDescription.Face = asset.id
elseif asset.assetType.name == "Shirt" then
humanoidDescription.Shirt = asset.id
elseif asset.assetType.name == "Pants" then
humanoidDescription.Pants = asset.id
elseif asset.assetType.name == "Head" then
humanoidDescription.Head = asset.id
elseif asset.assetType.name == "LeftArm" or asset.assetType.name == "Left Arm" then
humanoidDescription.LeftArm = asset.id
elseif asset.assetType.name == "LeftLeg" or asset.assetType.name == "Left Leg" then
humanoidDescription.LeftLeg = asset.id
elseif asset.assetType.name == "RightArm" or asset.assetType.name == "Right Arm" then
humanoidDescription.RightArm = asset.id
elseif asset.assetType.name == "RightLeg" or asset.assetType.name == "Right Leg" then
humanoidDescription.RightLeg = asset.id
elseif asset.assetType.name == "Torso" then
humanoidDescription.Torso = asset.id
elseif asset.assetType.name == "ClimbAnimation" or asset.assetType.name == "Climb Animation" then
humanoidDescription.ClimbAnimation = asset.id
elseif asset.assetType.name == "FallAnimation" or asset.assetType.name == "Fall Animation" then
humanoidDescription.FallAnimation = asset.id
elseif asset.assetType.name == "IdleAnimation" or asset.assetType.name == "Idle Animation" then
humanoidDescription.IdleAnimation = asset.id
elseif asset.assetType.name == "JumpAnimation" or asset.assetType.name == "Jump Animation" then
humanoidDescription.JumpAnimation = asset.id
elseif asset.assetType.name == "RunAnimation" or asset.assetType.name == "Run Animation" then
humanoidDescription.RunAnimation = asset.id
elseif asset.assetType.name == "SwimAnimation" or asset.assetType.name == "Swim Animation" then
humanoidDescription.SwimAnimation = asset.id
elseif asset.assetType.name == "WalkAnimation" or asset.assetType.name == "Walk Animation" then
humanoidDescription.WalkAnimation = asset.id
end
end
if humanoidDescription.Shirt == 0 then
humanoidDescription.Shirt = 855777286
end
if humanoidDescription.Pants == 0 then
humanoidDescription.Pants = 855782781
end
return humanoidDescription
end
local function playerAdded(player)
local function characterAdded(character)
local characterAppearanceInfo = playerWearingAssets[player]
if characterAppearanceInfo then
local humanoid = character:FindFirstChildWhichIsA("Humanoid") or character:WaitForChild("Humanoid")
local humanoidDescription = getHumanoidDescriptionFromCharacterAppearance(characterAppearanceInfo)
if not humanoid or not humanoid:IsDescendantOf(workspace) then
humanoid.AncestryChanged:Wait()
end
if humanoid.Health > 0 and humanoidDescription then
pcall(function()
humanoid:ApplyDescription(humanoidDescription)
end)
end
end
end
characterAdded(player.Character or player.CharacterAdded:Wait())
local characterAppearanceInfo
pcall(function()
characterAppearanceInfo = Players:GetCharacterAppearanceInfoAsync(player.UserId)
end)
playerWearingAssets[player] = characterAppearanceInfo or {}
player.CharacterAdded:Connect(characterAdded)
end
local function playerRemoved(player)
if playerWearingAssets[player] then
playerWearingAssets[player] = nil
end
end
for i, enumItem in ipairs(Enum.AssetType:GetEnumItems()) do
AssetTypeEnumLookup[enumItem.Value] = enumItem
end
for i, player in ipairs(Players:GetPlayers()) do
coroutine.wrap(playerAdded)(player)
end
Players.PlayerAdded:Connect(playerAdded)
Players.PlayerRemoving:Connect(playerRemoved)
remoteEvent.OnServerEvent:Connect(onServerEvent)