
KeylessUniversal
![BLOOD SYSTEM UNIVERSAL!! [Open soucre]](https://scriptblox.com/images/sb/0-1786977637846.jpg)
--// Servicios
local TS = game:GetService("TweenService")
local RunService = game:GetService("RunService")
local Debris = game:GetService("Debris")
local Players = game:GetService("Players")
local CollectionService = game:GetService("CollectionService")
local LocalPlayer = Players.LocalPlayer
--// Tweens
local tweenInfoIn = TweenInfo.new(0.2, Enum.EasingStyle.Exponential, Enum.EasingDirection.Out)
local tweenInfoOut = TweenInfo.new(1.5, Enum.EasingStyle.Linear, Enum.EasingDirection.Out)
local tweenSecado = TweenInfo.new(15, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
--// Texturas (Decals)
local bloodDecals = {
"rbxassetid://9893638760",
"rbxassetid://9893615086",
"rbxassetid://9893636391"
}
--// Sonidos 3D
local splatSounds = {
"rbxassetid://9114223178",
"rbxassetid://9114223280",
"rbxassetid://9114223405"
}
local deathSoundId = "rbxassetid://9114223553"
--// CONFIGURACIÓN
local bloodSettings = {
Duration = 30,
MaxDecals = 200
}
local bloodService = {}
local Pool = {}
local ActiveDrops = {}
local ActiveDecalsTrack = {}
local MAX_POOL_SIZE = 300
local newPartsFolder = workspace:FindFirstChild("BloodParticles") or Instance.new("Folder", workspace)
newPartsFolder.Name = "BloodParticles"
-------------------------------------------------------------------------------
-- UI: PANTALLA ENSANGRENTADA Y HUB
-------------------------------------------------------------------------------
local screenGui = Instance.new("ScreenGui")
screenGui.Name = "BloodScreenFX"
screenGui.ResetOnSpawn = false
if LocalPlayer then
local pGui = LocalPlayer:FindFirstChildOfClass("PlayerGui") or LocalPlayer:WaitForChild("PlayerGui", 5)
if pGui then screenGui.Parent = pGui end
end
local damageOverlay = Instance.new("Frame")
damageOverlay.Size = UDim2.new(1, 0, 1, 0)
damageOverlay.BackgroundColor3 = Color3.fromRGB(150, 0, 0)
damageOverlay.BackgroundTransparency = 1
damageOverlay.BorderSizePixel = 0
damageOverlay.Parent = screenGui
local function flashScreenDamage()
damageOverlay.BackgroundTransparency = 0.65
TS:Create(damageOverlay, TweenInfo.new(0.3), {BackgroundTransparency = 1}):Play()
end
local function createSettingsHUB()
local buttonContainer = Instance.new("Frame")
buttonContainer.Name = "BloodButtonContainer"
buttonContainer.Size = UDim2.new(0, 150, 0, 35)
buttonContainer.Position = UDim2.new(0, 15, 0.5, -17)
buttonContainer.BackgroundTransparency = 1
buttonContainer.Parent = screenGui
local toggleBtn = Instance.new("TextButton")
toggleBtn.Name = "BloodMenuToggle"
toggleBtn.Size = UDim2.new(1, -25, 1, 0)
toggleBtn.Position = UDim2.new(0, 0, 0, 0)
toggleBtn.BackgroundColor3 = Color3.fromRGB(100, 10, 10)
toggleBtn.TextColor3 = Color3.fromRGB(255, 255, 255)
toggleBtn.Text = "🩸 Sangre: 30s"
toggleBtn.Font = Enum.Font.SourceSansBold
toggleBtn.TextSize = 14
toggleBtn.Parent = buttonContainer
local toggleUICorner = Instance.new("UICorner")
toggleUICorner.CornerRadius = UDim.new(0, 8)
toggleUICorner.Parent = toggleBtn
local hubFrame = Instance.new("Frame")
hubFrame.Name = "BloodHUB"
hubFrame.Size = UDim2.new(0, 160, 0, 240)
hubFrame.Position = UDim2.new(0, 175, 0.5, -120)
hubFrame.BackgroundColor3 = Color3.fromRGB(25, 25, 25)
hubFrame.BackgroundTransparency = 0.1
hubFrame.Visible = false
hubFrame.Parent = screenGui
local closeBtn = Instance.new("TextButton")
closeBtn.Name = "CloseUI"
closeBtn.Size = UDim2.new(0, 20, 0, 35)
closeBtn.Position = UDim2.new(1, -20, 0, 0)
closeBtn.BackgroundColor3 = Color3.fromRGB(180, 30, 30)
closeBtn.TextColor3 = Color3.fromRGB(255, 255, 255)
closeBtn.Text = "X"
closeBtn.Font = Enum.Font.SourceSansBold
closeBtn.TextSize = 14
closeBtn.Parent = buttonContainer
local closeCorner = Instance.new("UICorner")
closeCorner.CornerRadius = UDim.new(0, 8)
closeCorner.Parent = closeBtn
closeBtn.MouseButton1Click:Connect(function()
buttonContainer.Visible = false
hubFrame.Visible = false
end)
local hubUICorner = Instance.new("UICorner")
hubUICorner.CornerRadius = UDim.new(0, 8)
hubUICorner.Parent = hubFrame
local title = Instance.new("TextLabel")
title.Size = UDim2.new(1, 0, 0, 30)
title.BackgroundTransparency = 1
title.Text = "Duración de Sangre"
title.TextColor3 = Color3.fromRGB(255, 255, 255)
title.Font = Enum.Font.SourceSansBold
title.TextSize = 14
title.Parent = hubFrame
local listLayout = Instance.new("UIListLayout")
listLayout.Padding = UDim.new(0, 5)
listLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
listLayout.SortOrder = Enum.SortOrder.LayoutOrder
listLayout.Parent = hubFrame
title.LayoutOrder = 0
local timeOptions = {
{text = "30 Segundos", time = 30, maxDecals = 200},
{text = "1 Minuto", time = 60, maxDecals = 200},
{text = "2 Minutos", time = 120, maxDecals = 200},
{text = "3 Minutos", time = 180, maxDecals = 200},
{text = "4 Minutos", time = 240, maxDecals = 200},
{text = "5 Minutos", time = 300, maxDecals = 400}
}
local buttons = {}
for i, option in ipairs(timeOptions) do
local btn = Instance.new("TextButton")
btn.Size = UDim2.new(0.9, 0, 0, 28)
btn.LayoutOrder = i
btn.Text = option.text
btn.Font = Enum.Font.SourceSans
btn.TextSize = 13
btn.Parent = hubFrame
local btnCorner = Instance.new("UICorner")
btnCorner.CornerRadius = UDim.new(0, 5)
btnCorner.Parent = btn
local function updateVisuals()
for _, bData in ipairs(buttons) do
if bData.time == bloodSettings.Duration then
bData.button.BackgroundColor3 = Color3.fromRGB(180, 20, 20)
bData.button.TextColor3 = Color3.fromRGB(255, 255, 255)
else
bData.button.BackgroundColor3 = Color3.fromRGB(45, 45, 45)
bData.button.TextColor3 = Color3.fromRGB(200, 200, 200)
end
end
toggleBtn.Text = "🩸 Sangre: " .. (bloodSettings.Duration >= 60 and (bloodSettings.Duration / 60 .. "m") or (bloodSettings.Duration .. "s"))
end
btn.MouseButton1Click:Connect(function()
bloodSettings.Duration = option.time
bloodSettings.MaxDecals = option.maxDecals
updateVisuals()
end)
table.insert(buttons, {button = btn, time = option.time})
end
for _, bData in ipairs(buttons) do
if bData.time == bloodSettings.Duration then
bData.button.BackgroundColor3 = Color3.fromRGB(180, 20, 20)
bData.button.TextColor3 = Color3.fromRGB(255, 255, 255)
else
bData.button.BackgroundColor3 = Color3.fromRGB(45, 45, 45)
bData.button.TextColor3 = Color3.fromRGB(200, 200, 200)
end
end
toggleBtn.MouseButton1Click:Connect(function()
hubFrame.Visible = not hubFrame.Visible
end)
end
createSettingsHUB()
-------------------------------------------------------------------------------
-- POOLING & CREACIÓN DE PIEZAS
-------------------------------------------------------------------------------
function createPart(): Part
local p: Part = Instance.new("Part")
p.Name = "BlPart"
p.Shape = Enum.PartType.Block
p.Size = Vector3.new(0.2, 0.2, 0.2)
p.Transparency = 1
p.CanCollide = false
p.Massless = true
p.Material = Enum.Material.SmoothPlastic
p.Anchored = false
p.CastShadow = false
local decal = Instance.new("Decal")
decal.Name = "Splatter"
decal.Face = Enum.NormalId.Top
decal.Transparency = 1
decal.Parent = p
local bgui = Instance.new("BillboardGui")
bgui.Name = "FlyingImage"
bgui.Size = UDim2.new(2.8, 0, 2.8, 0)
bgui.AlwaysOnTop = true
bgui.LightInfluence = 0.5
bgui.Parent = p
local img = Instance.new("ImageLabel")
img.Name = "Img"
img.Size = UDim2.new(1, 0, 1, 0)
img.BackgroundTransparency = 1
img.Parent = bgui
return p
end
local function GetPart()
if #Pool > 0 then
return table.remove(Pool)
end
return createPart()
end
local function ReturnPart(part: Part)
if not part then return end
ActiveDrops[part] = nil
part.Anchored = false
part.Transparency = 1
part.CanCollide = false
part.Massless = true
part.Parent = nil
part.Size = Vector3.new(0.2, 0.2, 0.2)
part.AssemblyLinearVelocity = Vector3.zero
part.AssemblyAngularVelocity = Vector3.zero
-- Limpieza estricta de soldaduras (WeldConstraints)
for _, child in ipairs(part:GetChildren()) do
if child:IsA("WeldConstraint") then
child:Destroy()
end
end
local decal = part:FindFirstChild("Splatter")
if decal then
decal.Transparency = 1
decal.Color3 = Color3.new(1, 1, 1)
end
local bgui = part:FindFirstChild("FlyingImage")
if bgui then bgui.Enabled = false end
for _, tag in ipairs(CollectionService:GetTags(part)) do
CollectionService:RemoveTag(part, tag)
end
if #Pool < MAX_POOL_SIZE then
table.insert(Pool, part)
else
part:Destroy()
end
end
local function RegisterNewDecal(part: Part)
for i, p in ipairs(ActiveDecalsTrack) do
if p == part then return end
end
table.insert(ActiveDecalsTrack, part)
if #ActiveDecalsTrack > bloodSettings.MaxDecals then
local oldestPart = table.remove(ActiveDecalsTrack, 1)
if oldestPart and oldestPart.Parent then
ReturnPart(oldestPart)
end
end
end
-------------------------------------------------------------------------------
-- EFECTOS VISUALES EXTRA
-------------------------------------------------------------------------------
local function playSplatSound(position: Vector3)
local soundPart = Instance.new("Part")
soundPart.Size = Vector3.new(0.1, 0.1, 0.1)
soundPart.Position = position
soundPart.Anchored = true
soundPart.Transparency = 1
soundPart.CanCollide = false
soundPart.Parent = workspace
local sound = Instance.new("Sound")
sound.SoundId = splatSounds[math.random(1, #splatSounds)]
sound.Volume = 0.7
sound.RollOffMaxDistance = 60
sound.Parent = soundPart
sound:Play()
Debris:AddItem(soundPart, 1.5)
end
local function createBloodMist(position: Vector3)
local mistPart = Instance.new("Part")
mistPart.Size = Vector3.new(0.1, 0.1, 0.1)
mistPart.Position = position
mistPart.Anchored = true
mistPart.Transparency = 1
mistPart.CanCollide = false
mistPart.Parent = newPartsFolder
local pe = Instance.new("ParticleEmitter")
pe.Color = ColorSequence.new(Color3.fromRGB(80 , 0, 0))
pe.Size = NumberSequence.new({NumberSequenceKeypoint.new(0, 1.5), NumberSequenceKeypoint.new(1, 5.0)})
pe.Texture = "rbxassetid://243664672"
pe.Transparency = NumberSequence.new({NumberSequenceKeypoint.new(0, 0.4), NumberSequenceKeypoint.new(1, 1)})
pe.Lifetime = NumberRange.new(0.3, 0.6)
pe.Rate = 0
pe.Speed = NumberRange.new(3, 8)
pe.SpreadAngle = Vector2.new(180, 180)
pe.Parent = mistPart
pe:Emit(10)
Debris:AddItem(mistPart, 1)
end
-------------------------------------------------------------------------------
-- BUCLE PRINCIPAL DE FÍSICA Y DETECCIÓN PRECISA DE IMPACTO
-------------------------------------------------------------------------------
RunService.Heartbeat:Connect(function(dt)
for part, data in pairs(ActiveDrops) do
if not part or not part.Parent then
ActiveDrops[part] = nil
continue
end
local currentPos = part.Position
local displacement = currentPos - data.lastPos
local dist = displacement.Magnitude
if dist > 0.001 then
local raycast = workspace:Raycast(data.lastPos, displacement, data.params)
if raycast then
data:Impact(raycast)
ActiveDrops[part] = nil
else
data.lastPos = currentPos
end
end
-- FAILSAFE: Si la gota se atora en el aire (velocidad 0 y sin impactar nada)
if ActiveDrops[part] and part.AssemblyLinearVelocity.Magnitude < 0.1 then
-- Chequeo final hacia abajo
local downRay = workspace:Raycast(part.Position, Vector3.new(0, -1, 0), data.params)
if downRay then
data:Impact(downRay)
else
-- Si de verdad se quedó flotando por un bug de Roblox, la reciclamos en vez de dejarla ahí
ReturnPart(part)
end
ActiveDrops[part] = nil
end
end
end)
-------------------------------------------------------------------------------
-- FUNCIÓN PRINCIPAL DE SANGRE
-------------------------------------------------------------------------------
function bloodService.CreateBlood(anchorPart: BasePart, parent, rate, particles, hitDirection)
if not anchorPart or not anchorPart:IsA("BasePart") or not particles then return end
local excluded = {parent, newPartsFolder}
for _, plyr in ipairs(Players:GetPlayers()) do
if plyr.Character then table.insert(excluded, plyr.Character) end
end
local rayParams = RaycastParams.new()
rayParams.FilterType = Enum.RaycastFilterType.Exclude
rayParams.FilterDescendantsInstances = excluded
rayParams.IgnoreWater = true
rayParams.RespectCanCollide = true
local rng = Random.new()
createBloodMist(anchorPart.Position)
for i = 1, particles do
local baseDir = hitDirection or (-anchorPart.CFrame.LookVector + Vector3.new(0, 0.5, 0))
if baseDir.Magnitude < 0.01 then baseDir = Vector3.new(0, 1, 0) end
local speed = rng:NextNumber(15, 40)
local spread = Vector3.new(rng:NextNumber(-8, 8), rng:NextNumber(-2, 10), rng:NextNumber(-8, 8))
local velocity = (baseDir.Unit * speed) + spread
local bloodDrop: Part = GetPart()
bloodDrop.Parent = newPartsFolder
local startPos = anchorPart.Position + Vector3.new(0, 0.3, 0)
bloodDrop.CFrame = CFrame.new(startPos)
bloodDrop.AssemblyLinearVelocity = velocity
local randomTextureId = bloodDecals[rng:NextInteger(1, #bloodDecals)]
local decal = bloodDrop:FindFirstChild("Splatter")
if decal then
decal.Texture = randomTextureId
decal.Color3 = Color3.new(1, 1, 1)
end
local bgui = bloodDrop:FindFirstChild("FlyingImage")
if bgui then
bgui.Enabled = true
local img = bgui:FindFirstChild("Img")
if img then
img.Image = randomTextureId
img.ImageColor3 = Color3.new(1, 1, 1)
end
end
ActiveDrops[bloodDrop] = {
part = bloodDrop,
lastPos = startPos,
params = rayParams,
Impact = function(self, raycast)
local part = self.part
local hitPart = raycast.Instance
if not part or not part.Parent then return end
if not hitPart or not hitPart:IsA("BasePart") then
ReturnPart(part)
return
end
-- FAILSAFE: Ignorar barreras completamente invisibles (Hitboxes, Triggers)
if hitPart.Transparency >= 1 and hitPart.Name ~= "HumanoidRootPart" then
ReturnPart(part)
return
end
playSplatSound(raycast.Position)
local sizeX = rng:NextNumber(5.0, 9.0)
local sizeZ = rng:NextNumber(3.5, 7.0)
-- YA NO SE ANCLA: Lo preparamos para ser soldado al objeto dinámico
part.Anchored = false
part.CanCollide = false
part.Massless = true
part.Transparency = 1
local bguiChild = part:FindFirstChild("FlyingImage")
if bguiChild then bguiChild.Enabled = false end
local decalChild = part:FindFirstChild("Splatter")
if decalChild then decalChild.Transparency = 0 end
part.CFrame = CFrame.lookAt(raycast.Position, raycast.Position + raycast.Normal)
* CFrame.Angles(math.rad(-90), 0, 0)
* CFrame.Angles(0, rng:NextNumber(0, math.pi * 2), 0)
-- SISTEMA DE WELDING: Pega la sangre al objeto/NPC que se está moviendo
local weld = Instance.new("WeldConstraint")
weld.Name = "BloodWeld"
weld.Part0 = part
weld.Part1 = hitPart
weld.Parent = part
TS:Create(part, tweenInfoIn, { Size = Vector3.new(sizeX, 0.05, sizeZ) }):Play()
if decalChild then
TS:Create(decalChild, tweenSecado, { Color3 = Color3.fromRGB(150, 150, 150) }):Play()
end
-- (Se retiró el efecto de que la sangre resbale hacia abajo porque causaba inestabilidad con los Welds de los NPCs en movimiento)
RegisterNewDecal(part)
task.delay(bloodSettings.Duration, function()
if not part or not part.Parent then return end
if decalChild then
local tween = TS:Create(decalChild, tweenInfoOut, {Transparency = 1})
tween:Play()
tween.Completed:Wait()
end
ReturnPart(part)
end)
end
}
task.delay(3, function()
if bloodDrop and ActiveDrops[bloodDrop] then ReturnPart(bloodDrop) end
end)
if rate and rate >= 0 then task.wait(rate) end
end
end
-------------------------------------------------------------------------------
-- CHARCO PROGRESIVO EN CADÁVERES
-------------------------------------------------------------------------------
local function createBodyPool(char: Model)
local root = char.PrimaryPart or char:FindFirstChild("HumanoidRootPart") or char:FindFirstChild("Torso")
if not root then return end
local rayParams = RaycastParams.new()
rayParams.FilterType = Enum.RaycastFilterType.Exclude
rayParams.FilterDescendantsInstances = {char, newPartsFolder}
rayParams.RespectCanCollide = true
local raycast = workspace:Raycast(root.Position, Vector3.new(0, -8, 0), rayParams)
if not raycast then return end
local hitPart = raycast.Instance
if not hitPart or hitPart.Transparency >= 1 then return end
local poolPart = Instance.new("Part")
poolPart.Name = "BloodPool"
poolPart.Size = Vector3.new(0.2, 0.05, 0.2)
poolPart.Anchored = false -- Preparado para soldadura (útil si mueren en un elevador móvil)
poolPart.CanCollide = false
poolPart.Massless = true
poolPart.Transparency = 1
poolPart.CFrame = CFrame.lookAt(raycast.Position, raycast.Position + raycast.Normal) * CFrame.Angles(math.rad(-90), 0, 0)
poolPart.Parent = newPartsFolder
-- Soldar el charco también por si el suelo resulta ser algo que se mueve
local weld = Instance.new("WeldConstraint")
weld.Part0 = poolPart
weld.Part1 = hitPart
weld.Parent = poolPart
local decal = Instance.new("Decal")
decal.Texture = bloodDecals[1]
decal.Face = Enum.NormalId.Top
decal.Color3 = Color3.new(1, 1, 1)
decal.Parent = poolPart
local targetSize = math.random(8, 15)
TS:Create(poolPart, TweenInfo.new(3, Enum.EasingStyle.Sine, Enum.EasingDirection.Out), {
Size = Vector3.new(targetSize, 0.05, targetSize)
}):Play()
TS:Create(decal, tweenSecado, { Color3 = Color3.fromRGB(150, 150, 150) }):Play()
RegisterNewDecal(poolPart)
task.delay(bloodSettings.Duration, function()
if poolPart and poolPart.Parent then
local tw = TS:Create(decal, tweenInfoOut, {Transparency = 1})
tw:Play()
tw.Completed:Wait()
poolPart:Destroy()
end
end)
end
-------------------------------------------------------------------------------
-- REGISTRO DE ENTIDADES (JUGADORES Y NPCS)
-------------------------------------------------------------------------------
function registerCharacter(character: Model)
if CollectionService:HasTag(character, "BloodSystemRegistered") then return end
CollectionService:AddTag(character, "BloodSystemRegistered")
local hum: Humanoid = character:FindFirstChildOfClass("Humanoid") or character:WaitForChild("Humanoid", 3)
if not hum then return end
hum:SetAttribute("OldHealth", hum.Health)
hum.HealthChanged:Connect(function(newHealth)
local oldHealth = hum:GetAttribute("OldHealth")
if oldHealth and oldHealth > newHealth then
if LocalPlayer and LocalPlayer.Character == character then
flashScreenDamage()
end
pcall(function()
bloodService.CreateBlood(character.PrimaryPart or character:FindFirstChild("HumanoidRootPart") or character:FindFirstChild("Torso"), character, 0, 2)
end)
end
hum:SetAttribute("OldHealth", newHealth)
end)
hum.Died:Connect(function()
local soundPart = Instance.new("Part")
soundPart.Position = character:GetPivot().Position
soundPart.Anchored = true soundPart.Transparency = 1 soundPart.Parent = workspace
local s = Instance.new("Sound") s.SoundId = deathSoundId s.Volume = 1 s.Parent = soundPart s:Play()
Debris:AddItem(soundPart, 2)
task.spawn(function()
local head = character:FindFirstChild("Head") or character.PrimaryPart
if head then
for burst = 1, 3 do
bloodService.CreateBlood(head, character, 0, 2)
task.wait(0.12)
end
end
end)
task.delay(0.4, function() createBodyPool(character) end)
end)
end
function registerPlayer(player: Player)
if CollectionService:HasTag(player, "BloodPlayerReg") then return end
CollectionService:AddTag(player, "BloodPlayerReg")
if player.Character then registerCharacter(player.Character) end
player.CharacterAdded:Connect(registerCharacter)
end
for _, currentPlayer in pairs(Players:GetPlayers()) do registerPlayer(currentPlayer) end
Players.PlayerAdded:Connect(registerPlayer)
workspace.DescendantAdded:Connect(function(descendant)
if descendant:IsA("Humanoid") then
local charModel = descendant.Parent
if charModel and charModel:IsA("Model") then
registerCharacter(charModel)
end
end
end)
for _, obj in ipairs(workspace:GetDescendants()) do
if obj:IsA("Humanoid") and obj.Parent and obj.Parent:IsA("Model") then
registerCharacter(obj.Parent)
end
endNo comments yet for this script.



