robloxexploits.lol
KeylessUniversal

Portal

sebas_7778 23 hours ago 358 views

Run it

Copy the script and paste it into your executor.

Portal.lua · 517 lines
--[[
	PORTAL (primera persona) - MÓVIL
	Colócalo como LocalScript en: StarterPlayer > StarterPlayerScripts

	- Fuerza la primera persona.
	- Muestra la pistola de portales y la vida (HEALTH) como en el videojuego.
	- Los portales se crean en la superficie a la que apuntas (pared, suelo o techo).
	  Si no apuntas a nada, usa la superficie más cercana a ti.
	- Conserva el impulso: si caes por un portal del suelo, sales con esa velocidad.
	- Los botones están junto al botón de saltar.
	- Solo tú los ves y usas (LocalScript).
]]

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")
local Debris = game:GetService("Debris")

local player = Players.LocalPlayer
local camera = workspace.CurrentCamera
local playerGui = player:WaitForChild("PlayerGui")
local A = CFrame.Angles

local char, humanoid, root

player.CameraMode = Enum.CameraMode.LockFirstPerson

local fx = Instance.new("Folder")
fx.Name = "LocalPortals"
fx.Parent = workspace

local H, W = 7.5, 5 -- alto y ancho del portal
local COLORS = {
	A = Color3.fromRGB(70, 160, 255),
	B = Color3.fromRGB(255, 140, 40),
}
local portals = {} -- portals.A / portals.B = { model, cf }

---------------------------------------------------------------------
-- UTILIDADES
---------------------------------------------------------------------
local function castParams()
	local p = RaycastParams.new()
	p.FilterType = Enum.RaycastFilterType.Exclude
	p.FilterDescendantsInstances = { char, fx }
	return p
end

local function camFlat()
	local l = camera.CFrame.LookVector
	local f = Vector3.new(l.X, 0, l.Z)
	return f.Magnitude > 0.01 and f.Unit or Vector3.new(0, 0, -1)
end

---------------------------------------------------------------------
-- INTERFAZ (botones junto al botón de saltar)
---------------------------------------------------------------------
local slot = playerGui:GetAttribute("HeroUISlots") or 0 -- evita solapar con mis otros scripts
playerGui:SetAttribute("HeroUISlots", slot + 1)
local BTN, GAP = 58, 8

local old = playerGui:FindFirstChild("PortalGui")
if old then old:Destroy() end

local gui = Instance.new("ScreenGui")
gui.Name = "PortalGui"
gui.ResetOnSpawn = false
gui.IgnoreGuiInset = true
gui.DisplayOrder = 100
gui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
gui.Parent = playerGui

local function dockNextToJump(frame)
	local function update()
		local vs = camera.ViewportSize
		local x, y = vs.X - 110, vs.Y - 16
		local tg = playerGui:FindFirstChild("TouchGui")
		local tcf = tg and tg:FindFirstChild("TouchControlFrame")
		local jb = tcf and tcf:FindFirstChild("JumpButton")
		if jb and jb.AbsoluteSize.X > 0 then
			x = jb.AbsolutePosition.X - 12
			y = jb.AbsolutePosition.Y + jb.AbsoluteSize.Y
		end
		frame.Position = UDim2.fromOffset(x - slot * (BTN + GAP + 4), y)
	end
	update()
	camera:GetPropertyChangedSignal("ViewportSize"):Connect(update)
	task.spawn(function()
		local tg = playerGui:WaitForChild("TouchGui", 15)
		if not tg then return end
		task.wait(1)
		update()
		local tcf = tg:WaitForChild("TouchControlFrame", 5)
		local jb = tcf and tcf:WaitForChild("JumpButton", 5)
		if jb then jb:GetPropertyChangedSignal("AbsolutePosition"):Connect(update) end
		update()
	end)
end

local column = Instance.new("Frame")
column.BackgroundTransparency = 1
column.AnchorPoint = Vector2.new(1, 1)
column.Size = UDim2.fromOffset(BTN, BTN * 3 + GAP * 2)
column.Parent = gui
local layout = Instance.new("UIListLayout")
layout.Padding = UDim.new(0, GAP)
layout.VerticalAlignment = Enum.VerticalAlignment.Bottom
layout.Parent = column
dockNextToJump(column)

local function makeButton(text, color)
	local b = Instance.new("TextButton")
	b.Size = UDim2.fromOffset(BTN, BTN)
	b.BackgroundColor3 = color
	b.BackgroundTransparency = 0.1
	b.TextColor3 = Color3.new(1, 1, 1)
	b.Font = Enum.Font.GothamBold
	b.TextSize = 13
	b.TextWrapped = true
	b.Text = text
	Instance.new("UICorner", b).CornerRadius = UDim.new(1, 0)
	b.Parent = column
	return b
end

local btnClear = makeButton(":wastebasket:\nBorrar", Color3.fromRGB(60, 60, 75))
local btnB = makeButton(":orange_circle:\nPortal B", Color3.fromRGB(210, 100, 20))
local btnA = makeButton(":blue_circle:\nPortal A", Color3.fromRGB(30, 100, 200))
-- (el orden de creación + alineación inferior deja A arriba, B en medio, Borrar abajo)

-- Mira estilo Portal: aro central + indicadores azul / naranja
local cross = Instance.new("Frame")
cross.AnchorPoint = Vector2.new(0.5, 0.5)
cross.Position = UDim2.fromScale(0.5, 0.5)
cross.Size = UDim2.fromOffset(26, 26)
cross.BackgroundTransparency = 1
Instance.new("UICorner", cross).CornerRadius = UDim.new(1, 0)
local cs = Instance.new("UIStroke", cross)
cs.Color = Color3.new(1, 1, 1)
cs.Thickness = 2
cross.Parent = gui

local function indicator(x, color)
	local f = Instance.new("Frame")
	f.AnchorPoint = Vector2.new(0.5, 0.5)
	f.Position = UDim2.new(0.5, x, 0.5, 0)
	f.Size = UDim2.fromOffset(12, 12)
	f.BackgroundColor3 = color
	f.BackgroundTransparency = 1
	Instance.new("UICorner", f).CornerRadius = UDim.new(1, 0)
	local s = Instance.new("UIStroke", f)
	s.Color = color
	s.Thickness = 2
	f.Parent = gui
	return f
end
local indA, indB = indicator(-26, COLORS.A), indicator(26, COLORS.B)

local function refreshCross()
	indA.BackgroundTransparency = portals.A and 0.05 or 1
	indB.BackgroundTransparency = portals.B and 0.05 or 1
end

-- Destello al teletransportarse
local flashFrame = Instance.new("Frame")
flashFrame.Size = UDim2.fromScale(1, 1)
flashFrame.BackgroundColor3 = Color3.new(1, 1, 1)
flashFrame.BackgroundTransparency = 1
flashFrame.BorderSizePixel = 0
flashFrame.ZIndex = 10
flashFrame.Parent = gui

local function flash()
	flashFrame.BackgroundTransparency = 0.35
	TweenService:Create(flashFrame, TweenInfo.new(0.4), { BackgroundTransparency = 1 }):Play()
end

-- HUD de vida estilo Portal / Half-Life
local ORANGE = Color3.fromRGB(255, 176, 0)
local hud = Instance.new("Frame")
hud.AnchorPoint = Vector2.new(0.5, 1)
hud.Position = UDim2.new(0.5, 0, 1, -14)
hud.Size = UDim2.fromOffset(170, 52)
hud.BackgroundColor3 = Color3.new(0, 0, 0)
hud.BackgroundTransparency = 0.45
Instance.new("UICorner", hud).CornerRadius = UDim.new(0, 8)
local hs = Instance.new("UIStroke", hud)
hs.Color = ORANGE
hs.Thickness = 1.5
hs.Transparency = 0.3
hud.Parent = gui

local hudLabel = Instance.new("TextLabel")
hudLabel.BackgroundTransparency = 1
hudLabel.Position = UDim2.fromOffset(10, 28)
hudLabel.Size = UDim2.fromOffset(80, 16)
hudLabel.Font = Enum.Font.GothamBold
hudLabel.TextSize = 13
hudLabel.TextColor3 = ORANGE
hudLabel.TextXAlignment = Enum.TextXAlignment.Left
hudLabel.Text = "HEALTH"
hudLabel.Parent = hud

local hudNum = Instance.new("TextLabel")
hudNum.BackgroundTransparency = 1
hudNum.Position = UDim2.fromOffset(70, 2)
hudNum.Size = UDim2.fromOffset(92, 48)
hudNum.Font = Enum.Font.GothamBold
hudNum.TextSize = 38
hudNum.TextColor3 = ORANGE
hudNum.TextXAlignment = Enum.TextXAlignment.Right
hudNum.Text = "100"
hudNum.Parent = hud

local hint = Instance.new("TextLabel")
hint.AnchorPoint = Vector2.new(0.5, 0)
hint.Position = UDim2.new(0.5, 0, 0, 50)
hint.Size = UDim2.fromOffset(380, 50)
hint.BackgroundTransparency = 1
hint.TextColor3 = Color3.new(1, 1, 1)
hint.TextStrokeTransparency = 0.4
hint.TextWrapped = true
hint.Font = Enum.Font.GothamMedium
hint.TextSize = 14
hint.Text = "Apunta con la mira y toca :blue_circle: o :orange_circle: para disparar un portal. Entra en uno para salir por el otro."
hint.Parent = gui
task.delay(12, function() hint.Visible = false end)

local function say(text)
	hint.Text = text
	hint.Visible = true
	task.delay(3, function()
		if hint.Text == text then hint.Visible = false end
	end)
end

---------------------------------------------------------------------
-- PISTOLA DE PORTALES (viewmodel en primera persona)
---------------------------------------------------------------------
local gunFolder = Instance.new("Folder")
gunFolder.Name = "PortalGun"
gunFolder.Parent = fx
local gunParts = {}

local function addGunPart(size, rel, color, material, shape)
	local p = Instance.new("Part")
	p.Size = size
	p.Color = color
	p.Material = material or Enum.Material.SmoothPlastic
	if shape then p.Shape = shape end
	p.Anchored, p.CanCollide, p.CanTouch, p.CanQuery, p.CastShadow = true, false, false, false, false
	p.Parent = gunFolder
	table.insert(gunParts, { part = p, rel = rel })
	return p
end

local white = Color3.fromRGB(240, 240, 242)
local grey = Color3.fromRGB(80, 80, 88)
local silver = Color3.fromRGB(190, 190, 198)

addGunPart(Vector3.new(0.34, 0.34, 1.3), CFrame.new(0, 0, 0), white)
addGunPart(Vector3.new(0.46, 0.5, 0.55), CFrame.new(0, -0.03, 0.8), grey)
addGunPart(Vector3.new(0.1, 0.52, 0.52), CFrame.new(0, 0, -0.35) * A(0, math.pi / 2, 0), silver,
	Enum.Material.Metal, Enum.PartType.Cylinder)
addGunPart(Vector3.new(0.1, 0.46, 0.46), CFrame.new(0, 0, -0.7) * A(0, math.pi / 2, 0), silver,
	Enum.Material.Metal, Enum.PartType.Cylinder)
for _, o in { Vector3.new(0.19, 0.1, 0), Vector3.new(-0.19, 0.1, 0), Vector3.new(0, -0.2, 0) } do
	addGunPart(Vector3.new(0.05, 0.05, 0.75), CFrame.new(o.X, o.Y, -1.0), silver, Enum.Material.Metal)
end
local tip = addGunPart(Vector3.new(0.24, 0.24, 0.24), CFrame.new(0, 0, -1.0), COLORS.A,
	Enum.Material.Neon, Enum.PartType.Ball)
local strip = addGunPart(Vector3.new(0.07, 0.07, 0.9), CFrame.new(0, 0.19, -0.05), COLORS.A, Enum.Material.Neon)

local recoil, tipColor = 0, COLORS.A

RunService:BindToRenderStep("PortalGunViewmodel", Enum.RenderPriority.Camera.Value + 1, function(dt)
	recoil = math.max(0, recoil - dt * 5)
	local speed = 0
	if root then
		local v = root.AssemblyLinearVelocity
		speed = Vector3.new(v.X, 0, v.Z).Magnitude
	end
	local bob = math.min(speed / 16, 1)
	local t = os.clock()
	local off = CFrame.new(0.8 + math.sin(t * 6) * 0.03 * bob,
		-0.75 + math.abs(math.cos(t * 6)) * 0.04 * bob, -1.4 + recoil * 0.35)
		* A(recoil * 0.25, -0.05, 0)
	local base = camera.CFrame * off
	for _, g in gunParts do g.part.CFrame = base * g.rel end
	tip.Color, strip.Color = tipColor, tipColor

	-- HUD de vida
	if humanoid then
		local hp = humanoid.Health
		hudNum.Text = (hp >= 1e6) and "∞" or tostring(math.max(0, math.ceil(hp)))
		local low = hp < 25
		hudNum.TextColor3 = low and Color3.fromRGB(255, 70, 40) or ORANGE
		hudLabel.TextColor3 = hudNum.TextColor3
	end
end)

---------------------------------------------------------------------
-- CREAR PORTALES
---------------------------------------------------------------------
local function buildPortal(key, cf)
	if portals[key] then portals[key].model:Destroy() end

	local model = Instance.new("Model")
	model.Name = "Portal" .. key
	model.Parent = fx
	local color = COLORS[key]

	local function makeCylinder(fullSize, c, transparency)
		local p = Instance.new("Part")
		p.Shape = Enum.PartType.Cylinder
		p.Size = Vector3.new(fullSize.X, 0.1, 0.1)
		p.Material = Enum.Material.Neon
		p.Color = c
		p.Transparency = transparency
		p.Anchored, p.CanCollide, p.CanTouch, p.CanQuery, p.CastShadow = true, false, false, false, false
		p.CFrame = cf * A(0, math.pi / 2, 0) -- el eje del cilindro mira hacia la normal de la superficie
		p.Parent = model
		TweenService:Create(p, TweenInfo.new(0.4, Enum.EasingStyle.Back, Enum.EasingDirection.Out),
			{ Size = fullSize }):Play()
		return p
	end

	makeCylinder(Vector3.new(0.2, H + 0.8, W + 0.8), color, 0.05) -- aro brillante
	local disc = makeCylinder(Vector3.new(0.35, H, W), color:Lerp(Color3.new(0, 0, 0), 0.55), 0.35)

	local e = Instance.new("ParticleEmitter")
	e.Color = ColorSequence.new(color, Color3.new(1, 1, 1))
	e.LightEmission = 1
	e.Size = NumberSequence.new({ NumberSequenceKeypoint.new(0, 0.6), NumberSequenceKeypoint.new(1, 0) })
	e.Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, 0.2), NumberSequenceKeypoint.new(1, 1) })
	e.Lifetime = NumberRange.new(0.6, 1)
	e.Rate = 45
	e.Speed = NumberRange.new(0.5, 2)
	e.SpreadAngle = Vector2.new(180, 180)
	e.RotSpeed = NumberRange.new(-120, 120)
	e.Parent = disc

	local light = Instance.new("PointLight")
	light.Color = color
	light.Brightness = 2
	light.Range = 14
	light.Parent = disc

	portals[key] = { model = model, cf = cf }
	refreshCross()
end

-- Marco del portal sobre una superficie (pared, suelo o techo)
local function surfaceFrame(pos, normal)
	pos = pos + normal * 0.06
	local up = Vector3.yAxis
	if math.abs(normal.Y) > 0.7 then
		up = camFlat() -- en suelo/techo el eje largo del portal sigue tu mirada, como en Portal
	else
		local g = workspace:Raycast(pos + Vector3.new(0, 1, 0), Vector3.new(0, -100, 0), castParams())
		if g and pos.Y - H / 2 < g.Position.Y then
			pos = Vector3.new(pos.X, g.Position.Y + H / 2 + 0.1, pos.Z)
		end
	end
	return CFrame.lookAt(pos, pos + normal, up)
end

-- Superficie a la que apuntas; si no hay, la más cercana a ti
local function findSurface()
	local params = castParams()
	local vp = camera.ViewportSize / 2
	local ray = camera:ViewportPointToRay(vp.X, vp.Y)
	local hit = workspace:Raycast(ray.Origin, ray.Direction * 250, params)
	if hit then return hit.Position, hit.Normal end

	local best
	for _, d in { Vector3.xAxis, -Vector3.xAxis, Vector3.yAxis, -Vector3.yAxis, Vector3.zAxis, -Vector3.zAxis } do
		local h = workspace:Raycast(root.Position, d * 60, params)
		if h and (not best or h.Distance < best.Distance) then best = h end
	end
	if best then return best.Position, best.Normal end
end

local function shootBall(from, to, color, onHit)
	local b = Instance.new("Part")
	b.Shape = Enum.PartType.Ball
	b.Size = Vector3.new(0.6, 0.6, 0.6)
	b.Material = Enum.Material.Neon
	b.Color = color
	b.Anchored, b.CanCollide, b.CanTouch, b.CanQuery = true, false, false, false
	b.Position = from
	b.Parent = fx
	local dur = math.clamp((to - from).Magnitude / 250, 0.05, 0.4)
	local tw = TweenService:Create(b, TweenInfo.new(dur, Enum.EasingStyle.Linear), { Position = to })
	tw.Completed:Connect(function()
		b:Destroy()
		onHit()
	end)
	tw:Play()
end

local function fire(key)
	if not root or not root.Parent then return end
	local pos, normal = findSurface()
	if not pos then
		say("No hay ninguna superficie cerca")
		return
	end
	tipColor = COLORS[key]
	recoil = 1
	local cf = surfaceFrame(pos, normal)
	shootBall(tip.Position, pos, COLORS[key], function() buildPortal(key, cf) end)
end

local function clearPortals()
	for key, data in portals do
		data.model:Destroy()
		portals[key] = nil
	end
	refreshCross()
	say("Portales borrados")
end

btnA.MouseButton1Click:Connect(function() fire("A") end)
btnB.MouseButton1Click:Connect(function() fire("B") end)
btnClear.MouseButton1Click:Connect(clearPortals)

---------------------------------------------------------------------
-- TELETRANSPORTE (detección local: solo tú puedes entrar)
---------------------------------------------------------------------
local lastTp, armed = 0, true

-- ¿Está tu personaje dentro de la zona de entrada del portal?
local function inside(data)
	local rel = data.cf:PointToObjectSpace(root.Position)
	if (rel.X / (W / 2)) ^ 2 + (rel.Y / (H / 2)) ^ 2 > 1 then return false end
	local d = -rel.Z -- distancia hacia fuera de la superficie
	local n = data.cf.LookVector
	if math.abs(n.Y) > 0.7 then
		if n.Y > 0 then return d < 4.3 and d > -1.5 end -- suelo: pisarlo
		return d < 3.3 and d > -1.5 -- techo: saltar hacia él
	end
	return math.abs(d) < 1.3 -- pared
end

local function teleport(from, to)
	lastTp = os.clock()
	local nOut = to.cf.LookVector

	-- Velocidad: del portal de entrada al de salida (giro de 180°)
	local vl = from.cf:VectorToObjectSpace(root.AssemblyLinearVelocity)
	vl = Vector3.new(-vl.X, vl.Y, -vl.Z)
	local vOut = to.cf:VectorToWorldSpace(vl)
	local minOut = (math.abs(nOut.Y) > 0.7) and 22 or 14
	local outSpeed = vOut:Dot(nOut)
	if outSpeed < minOut then vOut += nOut * (minOut - outSpeed) end

	-- Posición de salida
	local pos
	if math.abs(nOut.Y) > 0.7 then
		pos = to.cf.Position + nOut * 3.4
	else
		local flatOut = Vector3.new(nOut.X, 0, nOut.Z).Unit
		pos = to.cf.Position + flatOut * 2.4
		pos = Vector3.new(pos.X, to.cf.Position.Y - H / 2 + 3.2, pos.Z)
	end

	-- Giro de la cámara (para que mires hacia fuera del portal de salida)
	local rotMap = to.cf.Rotation * A(0, math.pi, 0) * from.cf.Rotation:Inverse()
	local newLook = rotMap * camera.CFrame.LookVector
	local camHeight = camera.CFrame.Position.Y - root.Position.Y

	local flat = Vector3.new(newLook.X, 0, newLook.Z)
	if flat.Magnitude < 0.1 then flat = camFlat() end
	root.CFrame = CFrame.lookAt(pos, pos + flat.Unit)
	root.AssemblyLinearVelocity = vOut
	root.AssemblyAngularVelocity = Vector3.zero

	local camPos = pos + Vector3.new(0, camHeight, 0)
	pcall(function() camera.CFrame = CFrame.lookAt(camPos, camPos + newLook) end)
	flash()
end

RunService.Heartbeat:Connect(function()
	if not root or not root.Parent then return end

	local hitKey
	for key, data in portals do
		if inside(data) then
			hitKey = key
			break
		end
	end
	if not hitKey then
		armed = true -- te has alejado: vuelve a activarse
		return
	end
	if not armed or os.clock() - lastTp < 0.4 then return end

	local other = portals[hitKey == "A" and "B" or "A"]
	if other then
		armed = false
		teleport(portals[hitKey], other)
	end
end)

---------------------------------------------------------------------
-- PERSONAJE / REAPARICIÓN
---------------------------------------------------------------------
local function setChar(c)
	char = c
	humanoid = c:WaitForChild("Humanoid")
	root = c:WaitForChild("HumanoidRootPart")
end

player.CharacterAdded:Connect(setChar)
setChar(player.Character or player.CharacterAdded:Wait())

About this script

Portal

More universal scripts