= 12 then inst[propName] = CFrame.new(pVal[1], pVal[2], pVal[3], pVal[4], pVal[5], pVal[6], pVal[7], pVal[8], pVal[9], pVal[10], pVal[11], pVal[12]) elseif #pVal >= 3 then inst[propName] = CFrame.new(pVal[1], pVal[2], pVal[3]) end end) elseif pType == "UDim2" and type(pVal) == "table" then pcall(function() inst[propName] = UDim2.new(pVal[1], pVal[2], pVal[3], pVal[4]) end) elseif pType == "Enum" and type(pVal) == "table" and pVal.EnumType and pVal.Name then pcall(function() inst[propName] = Enum[pVal.EnumType][pVal.Name] end) elseif pType == "Ref" and pVal then table.insert(pendingRefs, { inst = inst, prop = propName, targetId = pVal }) end end end end if node.Children and type(node.Children) == "table" then for _, childNode in ipairs(node.Children) do local childInst = constructNode(childNode) if childInst then pcall(function() childInst.Parent = inst end) end end end return inst end local rootInst = constructNode(rootNode) -- Segunda pasada GLOBAL: Resolver todas las referencias Ref (Part0, Part1, PrimaryPart) for _, refItem in ipairs(pendingRefs) do local targetInst = refs[refItem.targetId] if targetInst then pcall(function() refItem.inst[refItem.prop] = targetInst end) end end return rootInst end local function clearProducts(message) for _, child in products:GetChildren() do if not child:IsA("UIListLayout") and not child:IsA("UIPadding") then child:Destroy() end end if message then local empty = label(products, message, 13, colors.muted, 48) empty.TextXAlignment = Enum.TextXAlignment.Center end end -- In Studio, plugins should prefer DataModel:GetObjects. InsertService is kept -- as a fallback for assets owned or shared with the experience creator. local function loadAssetObjectsForPlugin(assetId) local getOk, objectsOrError = pcall(function() return game:GetObjects("rbxassetid://" .. tostring(assetId)) end) if getOk and type(objectsOrError) == "table" and #objectsOrError > 0 then return true, objectsOrError, nil end local insertOk, containerOrError = pcall(function() return InsertService:LoadAsset(assetId) end) if insertOk and containerOrError then local children = containerOrError:GetChildren() for _, child in ipairs(children) do child.Parent = nil end containerOrError:Destroy() if #children > 0 then return true, children, nil end end return false, nil, tostring(containerOrError or objectsOrError or "Roblox rejected the Asset ID") end -- The buyer may move an installed component out of the package folder during -- manual setup. Stamp the complete tree so the runtime certificate travels -- with every script and configuration module that needs it. local function stampLicenseTree(root, productId, licenseId) local function stamp(instance) instance:SetAttribute("PaulosProductId", tostring(productId)) if licenseId and tostring(licenseId) ~= "" then instance:SetAttribute("PaulosLicenseId", tostring(licenseId)) end end stamp(root) for _, descendant in ipairs(root:GetDescendants()) do stamp(descendant) end end local function renderProduct(product) local t = i18n[currentLang] local card = Instance.new("Frame") card.BackgroundColor3 = colors.panel card.Size = UDim2.new(1, -6, 0, 112) card.Parent = products rounded(card, 10) padding(card, 11) local layout = Instance.new("UIListLayout") layout.Padding = UDim.new(0, 4) layout.SortOrder = Enum.SortOrder.LayoutOrder layout.Parent = card local name = label(card, product.name or product.slug or "Producto", 14, colors.text, 20) name.Font = Enum.Font.GothamSemibold name.LayoutOrder = 1 local version = product.latestVersion and product.latestVersion.version or "v1.0.0" local versionLabel = label(card, "Version: " .. tostring(version), 12, colors.muted, 18) versionLabel.LayoutOrder = 2 local statusText = t.notAcquired local statusColor = colors.muted if product.activated then statusText = t.activeLicense statusColor = colors.success elseif product.acquired then statusText = t.acquiredNotActivated statusColor = colors.warning end local productStatus = label(card, statusText, 12, statusColor, 18) productStatus.LayoutOrder = 3 local installText = t.btnInsert if not product.activated then installText = product.acquired and t.btnActivateToInstall or t.btnRequiresPurchase end local install = button(card, installText, product.activated and colors.accent or colors.panelAlt) install.LayoutOrder = 4 install.Size = UDim2.new(1, 0, 0, 30) install.MouseButton1Click:Connect(function() if not product.activated then codeInput:CaptureFocus() if product.acquired then setStatus(t.activateHeader, colors.warning, t.activationRequiredMsg) else setStatus(t.notAcquired, colors.muted, t.btnRequiresPurchase) end return end setStatus(t.inserting, colors.warning, t.downloadingModel) install.Active = false task.spawn(function() local installPath = "/api/plugin/products/" .. tostring(product.id) .. "/install?nocache=" .. tostring(os.time()) local installOk, installData, statusCode = request("GET", installPath, nil, true) if not installOk then install.Active = true local reason = installData.message or installData.reason or installData.error or ("HTTP " .. tostring(statusCode)) setStatus(t.connectionError, colors.danger, tostring(reason)) return end local assetId = tonumber(tostring(installData.assetId or ""):match("%d+")) if not assetId then install.Active = true setStatus("Asset ID inválido", colors.danger, "El producto no tiene un Roblox Asset ID configurado.") return end local loadOk, loadedItems, loadError = loadAssetObjectsForPlugin(assetId) if not loadOk or not loadedItems then install.Active = true setStatus("No se pudo insertar", colors.danger, "Roblox rechazó el Asset ID " .. tostring(assetId) .. ". Revisa el modelo y sus permisos.") warn("[Paulo's Comms] Asset load failed:", tostring(loadError)) return end local moved = #loadedItems local installRoot if moved == 1 and loadedItems[1]:IsA("Folder") then -- Complete suites already provide their own root folder. Reuse it so -- buyers do not receive StagePropsPro_Suite inside another folder. installRoot = loadedItems[1] stampLicenseTree(installRoot, product.id, installData.licenseId) installRoot.Parent = workspace else installRoot = Instance.new("Folder") installRoot.Name = tostring(product.name or "PauloAsset") .. " (" .. (currentLang == "EN" and "Licensed" or "Licenciado") .. ")" installRoot:SetAttribute("PaulosProductId", tostring(product.id)) if installData.licenseId then installRoot:SetAttribute("PaulosLicenseId", tostring(installData.licenseId)) end installRoot.Parent = workspace for _, item in ipairs(loadedItems) do stampLicenseTree(item, product.id, installData.licenseId) item.Parent = installRoot end end if moved == 0 then if installRoot then installRoot:Destroy() end install.Active = true setStatus("Modelo vacío", colors.danger, "El Asset ID no contiene objetos insertables.") return end local badge = Instance.new("StringValue") badge.Name = "VerifiedLicense" badge.Value = "Paulo's Comms | Official licensed installation" badge.Parent = installRoot pcall(function() Selection:Set({ installRoot }) end) install.Active = true setStatus(t.modelBadgeInserted, colors.success, string.format(t.modelBadgeDetail, tostring(product.name or product.slug))) end) end) end -- Conservamos el bloque anterior solo como referencia; no se ejecuta ni se analiza. --[====[ local function renderProductLegacy(product) local t = i18n[currentLang] local card = Instance.new("Frame") card.BackgroundColor3 = colors.panel card.Size = UDim2.new(1, -6, 0, 112) card.Parent = products rounded(card, 10) padding(card, 11) local layout = Instance.new("UIListLayout") layout.Padding = UDim.new(0, 4) layout.SortOrder = Enum.SortOrder.LayoutOrder layout.Parent = card local name = label(card, product.name or product.slug, 14, colors.text, 20) name.Font = Enum.Font.GothamSemibold name.LayoutOrder = 1 local version = product.latestVersion and product.latestVersion.version or "v1.0.0" local versionLabel = label(card, "Version: " .. version, 12, colors.muted, 18) versionLabel.LayoutOrder = 2 local statusLabel if product.activated then statusLabel = label(card, t.activeLicense, 12, colors.success, 18) elseif product.acquired then statusLabel = label(card, t.acquiredNotActivated, 12, colors.warning, 18) else statusLabel = label(card, t.notAcquired, 12, colors.muted, 18) end statusLabel.LayoutOrder = 3 local installText = t.btnInsert if not product.activated then if product.acquired then installText = t.btnActivateToInstall else installText = t.btnRequiresPurchase end end local install = button(card, installText, product.activated and colors.accent or colors.panelAlt) install.LayoutOrder = 4 install.Size = UDim2.new(1, 0, 0, 30) install.MouseButton1Click:Connect(function() if not product.activated then codeInput:CaptureFocus() if product.acquired then setStatus(t.activateHeader, colors.warning, t.activationRequiredMsg) else setStatus(t.notAcquired, colors.muted, t.btnRequiresPurchase) end else setStatus(t.inserting, colors.warning, t.downloadingModel) task.spawn(function() local rawPath = "" if product.latestVersion and product.latestVersion.storage_object_path then rawPath = tostring(product.latestVersion.storage_object_path) elseif product.assetPath then rawPath = tostring(product.assetPath) end local insertedObj = nil local ok = false -- NUEVO SISTEMA HÍBRIDO (AssetId Directo de Roblox + Inyección de Licencia) ok, insertedObj = pcall(function() local root = nil local loadedAssetContainer = nil -- Intento 1: Usar directamente el AssetId de Roblox desde product.latestVersion local rawPath = "" if product.latestVersion and product.latestVersion.storage_object_path then rawPath = tostring(product.latestVersion.storage_object_path) elseif product.assetPath then rawPath = tostring(product.assetPath) end local digitsOnly = rawPath:gsub("%D+", "") local assetIdNum = tonumber(digitsOnly) if assetIdNum and assetIdNum > 0 then local loadOk, container = pcall(function() return InsertService:LoadAsset(assetIdNum) end) if loadOk and container then loadedAssetContainer = container root = container:FindFirstChildWhichIsA("Model") or container:GetChildren()[1] else warn("[Paulo's Comms] Notice InsertService:LoadAsset(" .. tostring(assetIdNum) .. "):", tostring(container)) end end -- Intento 2: Fallback al endpoint download-json (por si es un modelo JSON local) if not root then local path = "/api/plugin/products/" .. tostring(product.id) .. "/download-json?robloxUserId=" .. tostring(studioUserId or "") .. "&nocache=" .. tostring(os.time()) local reqSuccess, data = request("GET", path, nil, true) if reqSuccess and data then local rawAssetId = data.assetId or data.asset_id or data.asset if rawAssetId then local idDigits = tostring(rawAssetId):gsub("%D+", "") local secondAssetIdNum = tonumber(idDigits) if secondAssetIdNum and secondAssetIdNum > 0 then local loadOk, container = pcall(function() return InsertService:LoadAsset(secondAssetIdNum) end) if loadOk and container then loadedAssetContainer = container root = container:FindFirstChildWhichIsA("Model") or container:GetChildren()[1] end end end if not root and data.modelData then root = buildModelFromTree(data.modelData) end end end -- Intento 3: Fallback a la selección del Explorer if not root then local selected = Selection:Get() if #selected > 0 and selected[1]:IsA("Model") then root = selected[1] end end if not root or not root:IsA("Model") then setStatus("Error de Inserción", colors.danger, "No se pudo obtener el modelo de Roblox.") if loadedAssetContainer then loadedAssetContainer:Destroy() end return nil end -- Si vino de InsertService, sacarlo del contenedor if loadedAssetContainer then root.Parent = workspace loadedAssetContainer:Destroy() end -- INYECCIÓN DE SEGURIDAD Y MORPH AUTOMÁTICO local productId = tostring(product.id or "unknown") local verifyUrl = API_BASE_URL .. "/api/loader/verify" local licenseKey = tostring(data.licenseId or "") -- Limpiar scripts viejos for _, desc in ipairs(root:GetDescendants()) do if desc:IsA("Script") and (desc.Name == "PauloMorphLoader" or desc.Name == "PauloMorphServer" or desc.Name == "PauloMorphServerLogic") then desc:Destroy() end end -- 1. ClickDetector y ProximityPrompt en HumanoidRootPart local rootPartInst = root:FindFirstChild("HumanoidRootPart") or root:FindFirstChildWhichIsA("BasePart") if rootPartInst then if not rootPartInst:FindFirstChild("ClickDetector") then local cd = Instance.new("ClickDetector") cd.MaxActivationDistance = 15 cd.Parent = rootPartInst end if not rootPartInst:FindFirstChild("ProximityPrompt") then local pp = Instance.new("ProximityPrompt") pp.ActionText = "Transformarse" pp.ObjectText = root.Name pp.MaxActivationDistance = 10 pp.HoldDuration = 0.3 pp.RequiresLineOfSight = false pp.Parent = rootPartInst end end -- 2. RemoteEvent en ReplicatedStorage local ReplicatedStorage = game:GetService("ReplicatedStorage") local remoteName = "PauloMorphRemote_" .. tostring(product.id) local remote = ReplicatedStorage:FindFirstChild(remoteName) if not remote then remote = Instance.new("RemoteEvent") remote.Name = remoteName remote.Parent = ReplicatedStorage end -- 3. ScreenGui en StarterGui para el cliente local StarterGui = game:GetService("StarterGui") local guiName = "PauloMorphGui_" .. tostring(product.id) if not StarterGui:FindFirstChild(guiName) then local gui = Instance.new("ScreenGui") gui.Name = guiName gui.ResetOnSpawn = false gui.Enabled = false local frame = Instance.new("Frame") frame.Name = "MorphFrame" frame.Size = UDim2.new(0, 280, 0, 120) frame.Position = UDim2.fromScale(0.5, 0.5) frame.AnchorPoint = Vector2.new(0.5, 0.5) frame.BackgroundColor3 = Color3.fromRGB(25, 25, 30) frame.BorderSizePixel = 0 frame.Parent = gui rounded(frame, 12) local stroke = Instance.new("UIStroke") stroke.Color = Color3.fromRGB(114, 53, 234) stroke.Thickness = 2 stroke.Parent = frame local title = Instance.new("TextLabel") title.Size = UDim2.new(1, 0, 0, 30) title.Position = UDim2.new(0, 0, 0, 5) title.BackgroundTransparency = 1 title.Text = "Transformarse en: " .. root.Name title.TextColor3 = Color3.new(1, 1, 1) title.Font = Enum.Font.GothamBold title.TextSize = 14 title.Parent = frame local morphBtn = Instance.new("TextButton") morphBtn.Name = "MorphBtn" morphBtn.Size = UDim2.new(0, 110, 0, 40) morphBtn.Position = UDim2.new(0, 25, 0, 55) morphBtn.Text = "Morph" morphBtn.Font = Enum.Font.GothamBold morphBtn.TextSize = 16 morphBtn.TextColor3 = Color3.new(1, 1, 1) morphBtn.BackgroundColor3 = Color3.fromRGB(114, 53, 234) morphBtn.BorderSizePixel = 0 morphBtn.Parent = frame rounded(morphBtn, 8) local closeBtn = Instance.new("TextButton") closeBtn.Name = "CloseBtn" closeBtn.Size = UDim2.new(0, 110, 0, 40) closeBtn.Position = UDim2.new(0, 145, 0, 55) closeBtn.Text = "Cerrar" closeBtn.Font = Enum.Font.GothamBold closeBtn.TextSize = 16 closeBtn.TextColor3 = Color3.new(1, 1, 1) closeBtn.BackgroundColor3 = Color3.fromRGB(180, 30, 30) closeBtn.BorderSizePixel = 0 closeBtn.Parent = frame rounded(closeBtn, 8) local clientScript = Instance.new("LocalScript") clientScript.Name = "PauloMorphClient" clientScript.Source = [[ local RS = game:GetService("ReplicatedStorage") local remote = RS:WaitForChild("]] .. remoteName .. [[", 15) if not remote then return end local gui = script.Parent local frame = gui:WaitForChild("MorphFrame") local morphBtn = frame:WaitForChild("MorphBtn") local closeBtn = frame:WaitForChild("CloseBtn") remote.OnClientEvent:Connect(function(action) if action == "Open" then gui.Enabled = true end end) morphBtn.MouseButton1Click:Connect(function() gui.Enabled = false remote:FireServer("DoMorph") end) closeBtn.MouseButton1Click:Connect(function() gui.Enabled = false end) ]] clientScript.Parent = gui gui.Parent = StarterGui end -- 4. Inyectar PauloMorphServer (Script de servidor con morphing sin error de MeshId) local morphServer = Instance.new("Script") morphServer.Name = "PauloMorphServer" morphServer.Source = [[ -- PAULO'S COMMS: MORPH SERVER local model = script.Parent local RS = game:GetService("ReplicatedStorage") local remote = RS:WaitForChild("]] .. remoteName .. [[", 10) local function morphPlayer(player) local char = player.Character if not char then return end local charHumanoid = char:FindFirstChildWhichIsA("Humanoid") if not charHumanoid then return end for _, child in ipairs(model:GetChildren()) do if child:IsA("BasePart") and child.Name ~= "HumanoidRootPart" then local oldPart = char:FindFirstChild(child.Name) if oldPart then local newPart = child:Clone() for _, s in ipairs(newPart:GetDescendants()) do if s:IsA("BaseScript") then s:Destroy() end end for _, motor in ipairs(char:GetDescendants()) do if motor:IsA("Motor6D") then if motor.Part0 == oldPart then motor.Part0 = newPart end if motor.Part1 == oldPart then motor.Part1 = newPart end end end for _, motor in ipairs(newPart:GetDescendants()) do if motor:IsA("Motor6D") then if motor.Part0 == oldPart then motor.Part0 = newPart end if motor.Part1 == oldPart then motor.Part1 = newPart end end end newPart.Parent = char oldPart:Destroy() end end end for _, acc in ipairs(char:GetChildren()) do if acc:IsA("Accessory") then acc:Destroy() end end for _, acc in ipairs(model:GetChildren()) do if acc:IsA("Accessory") then acc:Clone().Parent = char end end for _, existing in ipairs(char:GetChildren()) do if existing:IsA("Shirt") or existing:IsA("Pants") or existing:IsA("ShirtGraphic") then existing:Destroy() end end for _, cloth in ipairs(model:GetChildren()) do if cloth:IsA("Shirt") or cloth:IsA("Pants") or cloth:IsA("ShirtGraphic") then cloth:Clone().Parent = char end end local modelHead = model:FindFirstChild("Head") local charHead = char:FindFirstChild("Head") if modelHead and charHead then for _, face in ipairs(charHead:GetChildren()) do if face:IsA("Decal") then face:Destroy() end end for _, face in ipairs(modelHead:GetChildren()) do if face:IsA("Decal") then face:Clone().Parent = charHead end end end local modelBC = model:FindFirstChildWhichIsA("BodyColors") if modelBC then local charBC = char:FindFirstChildWhichIsA("BodyColors") if charBC then charBC:Destroy() end modelBC:Clone().Parent = char end end if remote then remote.OnServerEvent:Connect(function(player, action) if action == "DoMorph" then morphPlayer(player) end end) end local prompt = model:FindFirstChildWhichIsA("ProximityPrompt", true) if prompt then prompt.Triggered:Connect(function(player) if remote then remote:FireClient(player, "Open") end end) end ]] morphServer.Parent = root -- 5. Inyectar Loader de Licencia local loaderScript = Instance.new("Script") loaderScript.Name = "PauloMorphLoader" loaderScript.Source = [==[ -- PAULO'S COMMS: GUARDIAN LOADER if not loadstring then warn("[Paulo's Comms] ERROR CRITICO: 'LoadStringEnabled' NO esta activado en ServerScriptService.") script.Parent:Destroy() return end local HttpService = game:GetService("HttpService") local VERIFY_URL = "]==] .. verifyUrl .. [==[" local PRODUCT_ID = "]==] .. productId .. [==[" local LICENSE_KEY = "]==] .. licenseKey .. [==[" local ok, response = pcall(function() return HttpService:PostAsync( VERIFY_URL, HttpService:JSONEncode({ licenseKey = LICENSE_KEY, productId = PRODUCT_ID, placeId = game.PlaceId }), Enum.HttpContentType.ApplicationJson ) end) if ok and response then local decoded = HttpService:JSONDecode(response) if decoded.valid and decoded.payload then local func, err = loadstring(decoded.payload) if func then func(script.Parent, LICENSE_KEY, PRODUCT_ID, VERIFY_URL) end else warn("[Paulo's Comms] Licencia invalida o revocada. Morph deshabilitado.") script.Parent:Destroy() end else warn("[Paulo's Comms] Fallo de conexion con los servidores de Paulo's Comms.") script.Parent:Destroy() end ]==] loaderScript.Parent = root return root and {root} or nil else warn("[Paulo's Comms] Error descargando modelo seguro:", response.StatusCode, response.Body) return nil end end) -- Create licensed wrapper folder local wrapperFolder = Instance.new("Folder") wrapperFolder.Name = tostring(product.name or "PauloAsset") .. " (" .. (currentLang == "EN" and "Licensed" or "Licenciado") .. ")" wrapperFolder.Parent = workspace -- Create verified license certificate badge StringValue local certBadge = Instance.new("StringValue") certBadge.Name = currentLang == "EN" and "VerifiedLicense" or "LicenciaVerificada" certBadge.Value = (currentLang == "EN" and "✓ Verified Paulo's Comms Official License for Roblox User ID: " or "✓ Licencia Oficial de Paulo's Comms verificada para Roblox User ID: ") .. tostring(studioUserId or "Licensed") .. " | Product: " .. tostring(product.name) certBadge.Parent = wrapperFolder -- Create README script for the buyer local readme = Instance.new("Script") readme.Name = "LEEME_IMPORTANTE" readme.Source = [[ -- ========================================== -- INSTRUCCIONES DE INSTALACIÓN DEL MORPH -- ========================================== -- ¡Hola! Gracias por adquirir este Morph oficial de Paulo's Comms. -- Para que este Morph funcione y pueda verificar su licencia, -- es OBLIGATORIO que actives la opción "LoadStringEnabled" en tu juego. -- PASOS A SEGUIR: -- 1. Ve a la pestaña "View" (Vista) arriba en Roblox Studio. -- 2. Abre las ventanas "Explorer" (Explorador) y "Properties" (Propiedades). -- 3. En el Explorador, busca una carpeta llamada "ServerScriptService" y dale click. -- 4. En la ventana de Propiedades (abajo), busca la opción que dice "LoadStringEnabled". -- 5. Márcala con una palomita (True). -- ¡Listo! Ahora solo dale Play al juego y el Morph se activará automáticamente. -- (Puedes borrar este script de LEEME_IMPORTANTE si quieres, no afecta en nada). ]] readme.Parent = wrapperFolder if ok and insertedObj and #insertedObj > 0 then for _, item in ipairs(insertedObj) do item.Parent = wrapperFolder end pcall(function() Selection:Set({ wrapperFolder }) end) setStatus(t.modelBadgeInserted, colors.success, string.format(t.modelBadgeDetail, tostring(product.name))) else if not ok then warn("[Paulo's Comms] Plugin Error:", tostring(insertedObj)) end pcall(function() Selection:Set({ wrapperFolder }) end) setStatus(t.badgeInsertedOnly, colors.success, string.format(t.badgeOnlyDetail, tostring(product.name))) end end) end end) end ]====] local function refreshProducts() local t = i18n[currentLang] if not token then clearProducts(t.pasteCodeHint) setStatus(t.noActiveSession, colors.muted, t.pasteCodeHint) disconnectButton.Visible = false return end setStatus(t.queryingProducts, colors.warning, t.verifyingLicenses) local queryPath = "/api/plugin/products" if studioUserId then queryPath = queryPath .. "?robloxUserId=" .. tostring(studioUserId) end local ok, data, statusCode = request("GET", queryPath, nil, true) if not ok then if statusCode == 401 then token = nil plugin:SetSetting(TOKEN_SETTING, nil) disconnectButton.Visible = false clearProducts(t.pasteCodeHint) setStatus(t.sessionExpired, colors.danger, t.sessionExpiredDetail) else clearProducts(t.connectionError) setStatus(t.connectionError, colors.danger, data.message or data.reason or data.error or ("HTTP " .. tostring(statusCode))) end return end clearProducts() for _, product in ipairs(data) do renderProduct(product) end if #data == 0 then clearProducts(t.noProductsYet) end if token then setStatus(t.licenseActive, colors.success, tostring(#data) .. " " .. t.readyToUse) disconnectButton.Visible = true else setStatus(t.restoredDetail, colors.warning, t.pasteCodeHint) disconnectButton.Visible = false end end local function updateTranslations() local t = i18n[currentLang] btnLangEn.BackgroundColor3 = currentLang == "EN" and colors.accent or colors.panelAlt btnLangEs.BackgroundColor3 = currentLang == "ES" and colors.accent or colors.panelAlt btnLangEn.TextColor3 = currentLang == "EN" and colors.text or colors.muted btnLangEs.TextColor3 = currentLang == "ES" and colors.text or colors.muted activateHeaderLabel.Text = t.activateHeader activateButton.Text = t.activateBtn disconnectButton.Text = t.logoutBtn productsHeader.Text = t.myProducts refreshProducts() end refreshBtn.MouseButton1Click:Connect(function() if token or studioUserId then refreshProducts() end end) btnLangEn.MouseButton1Click:Connect(function() if currentLang ~= "EN" then currentLang = "EN" updateTranslations() end end) btnLangEs.MouseButton1Click:Connect(function() if currentLang ~= "ES" then currentLang = "ES" updateTranslations() end end) disconnectButton.MouseButton1Click:Connect(function() local t = i18n[currentLang] if token then request("POST", "/api/plugin/session/revoke", {}, true) end token = nil plugin:SetSetting(TOKEN_SETTING, nil) disconnectButton.Visible = false setStatus(t.noActiveSession, colors.muted, t.pasteCodeHint) clearProducts(t.pasteCodeHint) end) activateButton.MouseButton1Click:Connect(function() local t = i18n[currentLang] local value = codeInput.Text if #value < 6 then setStatus(t.incompleteCode, colors.warning, t.incompleteCodeDetail) return end setStatus(t.verifyingCode, colors.warning, t.validatingServer) local ok, data = request("POST", "/api/plugin/activation/activate", { code = value, robloxUserId = studioUserId and tostring(studioUserId) or nil }, token ~= nil) if ok then codeInput.Text = "" if data.token then token = data.token plugin:SetSetting(TOKEN_SETTING, token) end setStatus(t.codeSuccess, colors.success, t.codeActivatedDetail) refreshProducts() else setStatus(t.codeFailed, colors.danger, data.message or data.reason or data.error or "Invalid code.") end end) toolbarButton.Click:Connect(function() widget.Enabled = not widget.Enabled if widget.Enabled then refreshProducts() end end) widget:GetPropertyChangedSignal("Enabled"):Connect(function() toolbarButton:SetActive(widget.Enabled) end) if token then task.defer(refreshProducts) else task.defer(refreshProducts) end PRNT END