stamper tool roblox
Key RequiredUniversal

stamper tool roblox

Universal ScriptSvbbbbbSvbbbbb 988 views 295 executesUpdated Aug 16, 2026
Unrated
00
Lua Script•364 lines (13603 chars)
--[[
	StamperTool - LocalScript
	Coloque este script em StarterPlayer > StarterPlayerScripts

	O que mudou nessa versão:
	1) CORRIGIDO: antes, ao clicar no botão, o jogo recalculava a posição
	   usando o mouse EM CIMA DO BOTÃO, então o bloco aparecia "atrás" dele.
	   Agora o preview PARA de se mover quando o mouse/dedo está sobre
	   qualquer parte da interface, então ele fica travado no último lugar
	   válido no mundo — e é ali que o bloco é colocado.
	2) NOVO: uma barra de seleção de textura (terra, grama, pedra, areia,
	   neve, gelo, madeira, metal, vidro, lava, luz) — clique numa opção
	   pra escolher o tipo de bloco antes de colocar.
]]

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local Workspace = game:GetService("Workspace")

local player = Players.LocalPlayer
local mouse = player:GetMouse()
local playerGui = player:WaitForChild("PlayerGui")

-- Se esse script já rodou antes (testou de novo sem parar o jogo, ou tem
-- uma cópia dele em outro lugar), remove a interface antiga primeiro.
-- Isso evita várias cópias transparentes empilhadas por cima uma da
-- outra — o que faria parecer que "tem algo na frente" bloqueando o
-- clique nos botões.
local oldGui = playerGui:FindFirstChild("StamperGui")
if oldGui then
	oldGui:Destroy()
end
local oldPreview = Workspace:FindFirstChild("StamperPreview")
if oldPreview then
	oldPreview:Destroy()
end

-- ================== CONFIGURAÇÕES ==================
local BLOCK_SIZE = Vector3.new(4, 4, 4)
local USE_GRID_SNAP = true
local GRID_SIZE = 4
local MAX_PLACE_DISTANCE = 100
-- =====================================================

-- ================== TIPOS DE BLOCO (TEXTURAS) ==================
local BLOCK_TYPES = {
	{ Name = "Terra",   Material = Enum.Material.Ground,      Color = Color3.fromRGB(115, 82, 54) },
	{ Name = "Grama",   Material = Enum.Material.Grass,       Color = Color3.fromRGB(74, 130, 51) },
	{ Name = "Pedra",   Material = Enum.Material.Slate,       Color = Color3.fromRGB(128, 128, 128) },
	{ Name = "Areia",   Material = Enum.Material.Sand,        Color = Color3.fromRGB(220, 200, 150) },
	{ Name = "Neve",    Material = Enum.Material.Snow,        Color = Color3.fromRGB(255, 255, 255) },
	{ Name = "Gelo",    Material = Enum.Material.Ice,         Color = Color3.fromRGB(200, 230, 255) },
	{ Name = "Madeira", Material = Enum.Material.WoodPlanks,  Color = Color3.fromRGB(130, 90, 50) },
	{ Name = "Metal",   Material = Enum.Material.Metal,       Color = Color3.fromRGB(180, 180, 190) },
	{ Name = "Vidro",   Material = Enum.Material.Glass,       Color = Color3.fromRGB(200, 230, 255), Transparency = 0.5 },
	{
		Name = "Lava", Material = Enum.Material.CrackedLava, Color = Color3.fromRGB(255, 80, 0),
		Light = { Color = Color3.fromRGB(255, 100, 0), Range = 12, Brightness = 3 },
	},
	{
		Name = "Luz", Material = Enum.Material.Neon, Color = Color3.fromRGB(255, 255, 200),
		Light = { Color = Color3.fromRGB(255, 255, 200), Range = 16, Brightness = 5 },
	},
}

local selectedType = BLOCK_TYPES[1]

-- Pasta pra guardar os blocos colocados
local placedFolder = Workspace:FindFirstChild("PlacedBlocks")
if not placedFolder then
	placedFolder = Instance.new("Folder")
	placedFolder.Name = "PlacedBlocks"
	placedFolder.Parent = Workspace
end

-- ================== BLOCO FANTASMA (PREVIEW) ==================
local previewPart = Instance.new("Part")
previewPart.Name = "StamperPreview"
previewPart.Size = BLOCK_SIZE
previewPart.Anchored = true
previewPart.CanCollide = false
previewPart.CanQuery = false
previewPart.Transparency = 0.5
previewPart.Material = Enum.Material.ForceField
previewPart.Color = Color3.fromRGB(0, 255, 100)
previewPart.Parent = Workspace

local isValidPlacement = true
local camera = Workspace.CurrentCamera

local function snap(position)
	if not USE_GRID_SNAP then
		return position
	end
	return Vector3.new(
		math.floor(position.X / GRID_SIZE + 0.5) * GRID_SIZE,
		math.floor(position.Y / GRID_SIZE + 0.5) * GRID_SIZE,
		math.floor(position.Z / GRID_SIZE + 0.5) * GRID_SIZE
	)
end

local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
raycastParams.FilterDescendantsInstances = { previewPart }

local isDeleteMode = false
local lastHitInstance = nil -- a peça (do mundo ou já colocada) que a mira está tocando agora

-- Sistema de "mira central": em vez de depender de onde o mouse/dedo está
-- tocando (que no celular é pouco confiável e conflita com a interface),
-- o preview segue sempre o CENTRO da câmera, tipo uma mira. Pra mirar,
-- o jogador só precisa girar a câmera (arrastar o dedo em qualquer lugar
-- da tela) — não precisa acertar um ponto exato na tela.
RunService.RenderStepped:Connect(function()
	local character = player.Character
	if not character then
		return
	end

	raycastParams.FilterDescendantsInstances = { previewPart, character }

	local origin = camera.CFrame.Position
	local direction = camera.CFrame.LookVector * MAX_PLACE_DISTANCE
	local result = Workspace:Raycast(origin, direction, raycastParams)

	if result then
		lastHitInstance = result.Instance
	else
		lastHitInstance = nil
	end

	if isDeleteMode then
		-- modo apagar: a mira "abraça" o bloco colocado que está na frente,
		-- em vez de mostrar um preview de bloco novo
		local canDelete = lastHitInstance ~= nil and lastHitInstance:IsDescendantOf(placedFolder)
		if canDelete then
			previewPart.Size = lastHitInstance.Size + Vector3.new(0.2, 0.2, 0.2)
			previewPart.CFrame = lastHitInstance.CFrame
			previewPart.Color = Color3.fromRGB(255, 60, 60)
		else
			previewPart.Size = BLOCK_SIZE
			local hitPosition = result and (result.Position + result.Normal * (BLOCK_SIZE.Y / 2))
				or (origin + camera.CFrame.LookVector * MAX_PLACE_DISTANCE)
			previewPart.CFrame = CFrame.new(snap(hitPosition))
			previewPart.Color = Color3.fromRGB(120, 120, 120) -- nada pra apagar aqui
		end
		isValidPlacement = canDelete
		return
	end

	previewPart.Size = BLOCK_SIZE
	local hitPosition
	if result then
		-- assenta o bloco em cima da superfície atingida (funciona em
		-- chão, parede ou teto, seguindo a normal da superfície)
		hitPosition = result.Position + result.Normal * (BLOCK_SIZE.Y / 2)
		isValidPlacement = true
	else
		-- nada na frente: mostra o preview no limite da distância máxima,
		-- só como indicação visual, e marca como inválido
		hitPosition = origin + camera.CFrame.LookVector * MAX_PLACE_DISTANCE
		isValidPlacement = false
	end

	previewPart.CFrame = CFrame.new(snap(hitPosition))
	previewPart.Color = isValidPlacement and selectedType.Color or Color3.fromRGB(255, 60, 60)
end)

-- ================== INTERFACE ==================
local screenGui = Instance.new("ScreenGui")
screenGui.Name = "StamperGui"
screenGui.ResetOnSpawn = false
-- IgnoreGuiInset FICA DESLIGADO (false): assim o Roblox empurra nossa
-- interface pra baixo da barra de topo padrão (ícones de menu/chat),
-- em vez de ficar escondida atrás dela.
screenGui.IgnoreGuiInset = false
screenGui.DisplayOrder = -10 -- fica ATRÁS da interface/HUD do jogo, em vez de na frente
screenGui.Parent = playerGui

-- Mira central (mostra pra onde a câmera está apontando)
local reticle = Instance.new("Frame")
reticle.Name = "Reticle"
reticle.Size = UDim2.new(0, 8, 0, 8)
reticle.AnchorPoint = Vector2.new(0.5, 0.5)
reticle.Position = UDim2.new(0.5, 0, 0.5, 0)
reticle.BackgroundColor3 = Color3.new(1, 1, 1)
reticle.BackgroundTransparency = 0.3
reticle.ZIndex = 50
reticle.Parent = screenGui
local reticleCorner = Instance.new("UICorner")
reticleCorner.CornerRadius = UDim.new(1, 0)
reticleCorner.Parent = reticle

-- Botão de colocar bloco (canto superior direito — longe do joystick e do
-- botão de pulo do celular)
local placeButton = Instance.new("TextButton")
placeButton.Name = "PlaceButton"
placeButton.Size = UDim2.new(0, 150, 0, 55)
placeButton.Position = UDim2.new(1, -170, 0, 45)
placeButton.BackgroundColor3 = Color3.fromRGB(40, 40, 40)
placeButton.TextColor3 = Color3.new(1, 1, 1)
placeButton.Font = Enum.Font.GothamBold
placeButton.TextSize = 20
placeButton.Text = "Colocar Bloco"
placeButton.AutoButtonColor = true
placeButton.ZIndex = 50
placeButton.Parent = screenGui

local placeCorner = Instance.new("UICorner")
placeCorner.CornerRadius = UDim.new(0, 12)
placeCorner.Parent = placeButton

-- Botão de alternar modo (Colocar / Apagar), logo abaixo do botão principal
local modeButton = Instance.new("TextButton")
modeButton.Name = "ModeButton"
modeButton.Size = UDim2.new(0, 150, 0, 45)
modeButton.Position = UDim2.new(1, -170, 0, 110)
modeButton.BackgroundColor3 = Color3.fromRGB(60, 60, 60)
modeButton.TextColor3 = Color3.new(1, 1, 1)
modeButton.Font = Enum.Font.GothamBold
modeButton.TextSize = 16
modeButton.Text = "Modo: Colocar"
modeButton.AutoButtonColor = true
modeButton.ZIndex = 50
modeButton.Parent = screenGui

local modeCorner = Instance.new("UICorner")
modeCorner.CornerRadius = UDim.new(0, 12)
modeCorner.Parent = modeButton

-- Barra de seleção de texturas: coluna vertical rolável no MEIO da tela,
-- lado esquerdo — essa é a única região que ficou livre em todos os
-- prints que você mandou (topo tem o HUD do jogo e ícones, lado direito
-- tem o placar, embaixo tem os botões Menu/Skins e os controles)
local textureFrame = Instance.new("ScrollingFrame")
textureFrame.Name = "TextureBar"
textureFrame.Size = UDim2.new(0, 140, 0, 320)
textureFrame.AnchorPoint = Vector2.new(0, 0.5)
textureFrame.Position = UDim2.new(0, 20, 0.5, 0)
textureFrame.ScrollingDirection = Enum.ScrollingDirection.Y
textureFrame.AutomaticCanvasSize = Enum.AutomaticSize.Y
textureFrame.CanvasSize = UDim2.new(0, 0, 0, 0)
textureFrame.ScrollBarThickness = 6
textureFrame.ZIndex = 50
textureFrame.BackgroundColor3 = Color3.fromRGB(25, 25, 25)
textureFrame.BackgroundTransparency = 0.1
textureFrame.Parent = screenGui

local textureCorner = Instance.new("UICorner")
textureCorner.CornerRadius = UDim.new(0, 12)
textureCorner.Parent = textureFrame

local listLayout = Instance.new("UIListLayout")
listLayout.FillDirection = Enum.FillDirection.Vertical
listLayout.Padding = UDim.new(0, 8)
listLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
listLayout.SortOrder = Enum.SortOrder.LayoutOrder
listLayout.Parent = textureFrame

local padding = Instance.new("UIPadding")
padding.PaddingLeft = UDim.new(0, 10)
padding.PaddingRight = UDim.new(0, 10)
padding.PaddingTop = UDim.new(0, 10)
padding.PaddingBottom = UDim.new(0, 10)
padding.Parent = textureFrame

local textureButtons = {}

local function refreshSelection()
	for _, entry in ipairs(textureButtons) do
		local isSelected = entry.blockType == selectedType
		entry.stroke.Thickness = isSelected and 3 or 1
		entry.stroke.Color = isSelected and Color3.fromRGB(0, 255, 100) or Color3.fromRGB(255, 255, 255)
	end
end

for i, blockType in ipairs(BLOCK_TYPES) do
	local button = Instance.new("TextButton")
	button.Name = blockType.Name
	button.LayoutOrder = i
	button.Size = UDim2.new(0, 70, 0, 70)
	button.BackgroundColor3 = blockType.Color
	button.BackgroundTransparency = 0
	button.AutoButtonColor = true
	button.Text = blockType.Name
	button.TextSize = 13
	button.Font = Enum.Font.GothamBold
	button.TextColor3 = Color3.new(1, 1, 1)
	button.ZIndex = 51
	button.TextStrokeTransparency = 0
	button.Parent = textureFrame

	local btnCorner = Instance.new("UICorner")
	btnCorner.CornerRadius = UDim.new(0, 10)
	btnCorner.Parent = button

	local stroke = Instance.new("UIStroke")
	stroke.Thickness = 1
	stroke.Color = Color3.fromRGB(255, 255, 255)
	stroke.Parent = button

	-- .Activated funciona de forma confiável com clique E com toque,
	-- inclusive dentro de uma ScrollingFrame (MouseButton1Click às vezes
	-- falhava no celular se o dedo tremesse um pouco durante o toque)
	button.Activated:Connect(function()
		selectedType = blockType
		refreshSelection()
	end)

	table.insert(textureButtons, { button = button, stroke = stroke, blockType = blockType })
end

refreshSelection()

-- ================== ALTERNAR MODO (COLOCAR / APAGAR) ==================
modeButton.Activated:Connect(function()
	isDeleteMode = not isDeleteMode
	if isDeleteMode then
		modeButton.Text = "Modo: Apagar"
		modeButton.BackgroundColor3 = Color3.fromRGB(120, 40, 40)
		placeButton.Text = "Apagar Bloco"
		placeButton.BackgroundColor3 = Color3.fromRGB(120, 40, 40)
	else
		modeButton.Text = "Modo: Colocar"
		modeButton.BackgroundColor3 = Color3.fromRGB(60, 60, 60)
		placeButton.Text = "Colocar Bloco"
		placeButton.BackgroundColor3 = Color3.fromRGB(40, 40, 40)
	end
end)

-- ================== FUNÇÃO DO BOTÃO PRINCIPAL (COLOCAR OU APAGAR) ==================
placeButton.Activated:Connect(function()
	if isDeleteMode then
		-- só apaga se o que está na mira for um bloco que essa ferramenta colocou
		if lastHitInstance and lastHitInstance:IsDescendantOf(placedFolder) then
			lastHitInstance:Destroy()
		end
		return
	end

	if not isValidPlacement then
		return
	end

	local newBlock = Instance.new("Part")
	newBlock.Size = BLOCK_SIZE
	newBlock.CFrame = previewPart.CFrame
	newBlock.Anchored = true
	newBlock.CanCollide = true
	newBlock.Material = selectedType.Material
	newBlock.Color = selectedType.Color
	newBlock.Transparency = selectedType.Transparency or 0
	newBlock.Parent = placedFolder

	-- se o tipo escolhido tem luz (lava, bloco de luz), adiciona o PointLight
	if selectedType.Light then
		local light = Instance.new("PointLight")
		light.Color = selectedType.Light.Color
		light.Range = selectedType.Light.Range
		light.Brightness = selectedType.Light.Brightness
		light.Parent = newBlock
	end
end)

Features & Information

stamper tool create things

Community Comments (0)

No comments yet for this script.

More Scripts for Universal Scripts