--!nocheck -- Kill effect v2: Piano Drop, on the v2 stage (run stage.luau first). -- A real mesh grand piano (asset 4689747350) drops from 30 studs onto the R15 victim: a soft shadow grows while -- the victim looks up, ease-in fall, impact on frame 4 of 12 with a one-frame 85% flash, the body flattened under -- it with dusty legs poking out, warm grey smoke_main dust, loose keys tumbling, one bounce, decaying wobble, -- three small mint notes drifting up. Banner PIANO'D +150. local SS = game:GetService("ServerStorage") if _G.LabFX then pcall(_G.LabFX.cleanup) end -- restore the victim before remembering its rest pose local old = workspace:FindFirstChild("LabFX") if old then old:Destroy() end local og = game.StarterGui:FindFirstChild("LabFXOverlay") if og then og:Destroy() end local root = Instance.new("Folder") root.Name = "LabFX" root.Parent = workspace local PAPER = Color3.fromRGB(250, 246, 237) local MINT = Color3.fromRGB(35, 232, 162) local CHAR = Color3.fromRGB(27, 25, 24) local DUST = Color3.fromRGB(150, 142, 132) local victim = workspace.LabStage.Victim local hrp, head = victim.HumanoidRootPart, victim.Head local base = Vector3.new(hrp.Position.X, 0.2, hrp.Position.Z) local rng = Random.new(7) local rad = math.rad local function part(props) local p = Instance.new("Part") p.Anchored = true p.CanCollide = false p.CastShadow = false p.TopSurface = Enum.SurfaceType.Smooth p.BottomSurface = Enum.SurfaceType.Smooth for k, v in pairs(props) do p[k] = v end p.Parent = root return p end -- Camera first: everything is laid out relative to its horizontal direction. local cam = workspace.CurrentCamera local camCF = CFrame.lookAt(base + Vector3.new(7.5, 5.2, 9.5), base + Vector3.new(-0.5, 2.2, -0.6)) local toCam = Vector3.new(camCF.Position.X - base.X, 0, camCF.Position.Z - base.Z).Unit local side = toCam:Cross(Vector3.yAxis) -- screen-left to screen-right along the floor -- Piano: the inserted asset is kept as a template in ServerStorage so re-running the stage does not lose it. local tpl = SS:FindFirstChild("LabPianoTemplate") if not tpl then local src = workspace.LabStage:FindFirstChild("GrandPiano") assert(src, "insert asset 4689747350 into workspace.LabStage as GrandPiano first") for _, d in ipairs(src:GetDescendants()) do if d:IsA("LuaSourceContainer") or d:IsA("JointInstance") then d:Destroy() end end local _, s = src:GetBoundingBox() src:ScaleTo(src:GetScale() * 7 / math.max(s.X, s.Z)) src.Name = "LabPianoTemplate" src.Parent = SS tpl = src end local piano = tpl:Clone() piano.Parent = root local bbCF, bbSize = piano:GetBoundingBox() -- bb LookVector points at the keyboard end, Z is the long axis local pparts, biggest = {}, nil for _, d in ipairs(piano:GetDescendants()) do if d:IsA("BasePart") then d.Anchored, d.CanCollide = true, false pparts[#pparts + 1] = { p = d, rel = bbCF:ToObjectSpace(d.CFrame) } if not biggest or d.Size.Magnitude > biggest.Size.Magnitude then biggest = d end end end for _, e in ipairs(pparts) do local d = e.p if d == biggest then d.Color, d.Material, d.Reflectance = Color3.fromRGB(16, 15, 17), Enum.Material.SmoothPlastic, 0 elseif d.Size.Magnitude < 2 then d.Color, d.Material = Color3.fromRGB(176, 146, 84), Enum.Material.Metal -- pedals, brass elseif d.Color.R > 0.5 then d.Color, d.Material = PAPER, Enum.Material.Plastic -- white keys else d.Color, d.Material = CHAR, Enum.Material.Plastic -- black keys end end -- Long axis across the screen, keyboard to screen-left; centred a little behind the victim so the legs poke out. local restY = bbSize.Y / 2 + 0.2 - 0.75 -- the legs buckle into the floor, the case sits on the body local pianoBase = CFrame.lookAt(Vector3.zero, -side) + (Vector3.new(base.X, 0, base.Z) - toCam * 1.3) local function posePiano(cf) for _, e in ipairs(pparts) do e.p.CFrame = cf * e.rel end end -- Victim: remember the rest pose of every part. local restRoot = hrp.CFrame local bodyParts = {} for _, d in ipairs(victim:GetDescendants()) do if d:IsA("BasePart") and d.Name ~= "HumanoidRootPart" then bodyParts[#bodyParts + 1] = { p = d, color = d.Color, mat = d.Material, cf = d.CFrame, size = d.Size, rel = restRoot:ToObjectSpace(d.CFrame) } end end local neckPivot = head.CFrame * CFrame.new(0, -0.5, 0) -- Lying frame: rig up points away from the camera (head under the piano), rig back faces up, feet toward camera. local lie = CFrame.lookAt(Vector3.zero, -toCam) * CFrame.Angles(rad(-90), 0, 0) + (base + Vector3.new(0, 0.14, 0) - toCam * 1.1) -- Shadow: soft dark Slate disc on the floor, grows before impact, hands over to the real shadow after. local shadow = part({ Shape = Enum.PartType.Cylinder, Size = Vector3.new(0.04, 0.1, 0.1), Material = Enum.Material.Slate, Color = Color3.fromRGB(20, 19, 18), Transparency = 1 }) local shadowAt = CFrame.new(base.X - toCam.X * 0.5, 0.23, base.Z - toCam.Z * 0.5) * CFrame.Angles(0, 0, rad(90)) -- Dust: smoke_main in warm grey from a floor emitter, re-emitted each step after impact with a decaying count. local dustPart = part({ Size = Vector3.new(5, 0.2, 3), Transparency = 1, CFrame = CFrame.new(pianoBase.Position.X, 0.4, pianoBase.Position.Z) }) local dust = Instance.new("ParticleEmitter") dust.Texture = "rbxasset://textures/particles/smoke_main.dds" dust.Color = ColorSequence.new(Color3.fromRGB(168, 156, 140), Color3.fromRGB(126, 118, 108)) dust.Size = NumberSequence.new({ NumberSequenceKeypoint.new(0, 1.6), NumberSequenceKeypoint.new(1, 4.4) }) dust.Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, 0.15), NumberSequenceKeypoint.new(0.4, 0.45), NumberSequenceKeypoint.new(1, 1) }) dust.Lifetime = NumberRange.new(0.9, 1.6) dust.Rate = 0 dust.Speed = NumberRange.new(4, 9) dust.SpreadAngle = Vector2.new(80, 80) dust.EmissionDirection = Enum.NormalId.Top dust.Acceleration = Vector3.new(0, -1.5, 0) dust.Drag = 2.5 dust.Rotation = NumberRange.new(0, 360) dust.RotSpeed = NumberRange.new(-40, 40) dust.Parent = dustPart -- Loose keys: paper and charcoal Plastic blocks thrown out of the keyboard end on ballistic arcs, then lying flat. local keys = {} local kbEnd = pianoBase.Position + (-side) * 3.1 + Vector3.new(0, 1.6, 0) for i = 1, 7 do local white = i % 3 ~= 0 local k = part({ Size = white and Vector3.new(0.24, 0.14, 0.95) or Vector3.new(0.15, 0.16, 0.6), Material = Enum.Material.Plastic, Color = white and PAPER or CHAR, Transparency = 1, CastShadow = true }) local dir = (-side * rng:NextNumber(0.4, 1) + toCam * rng:NextNumber(0.1, 1.1)).Unit keys[i] = { p = k, v = dir * rng:NextNumber(5, 9) + Vector3.new(0, rng:NextNumber(5, 9), 0), spin = Vector3.new(rng:NextNumber(-14, 14), rng:NextNumber(-10, 10), rng:NextNumber(-14, 14)), yaw = rng:NextNumber(0, 6.28), from = kbEnd + side * rng:NextNumber(-0.3, 0.3) } end -- Music notes: three small mint Plastic notes (flat head, thin stem, flag), turned to the camera. local notes = {} for i = 1, 3 do local m = { head = part({ Size = Vector3.new(0.34, 0.26, 0.06), Material = Enum.Material.Plastic, Color = MINT, Transparency = 1 }), stem = part({ Size = Vector3.new(0.06, 0.62, 0.06), Material = Enum.Material.Plastic, Color = MINT, Transparency = 1 }), flag = part({ Size = Vector3.new(0.22, 0.07, 0.06), Material = Enum.Material.Plastic, Color = MINT, Transparency = 1 }), at = pianoBase.Position + side * (i - 2) * 1.1 + toCam * 0.6 + Vector3.new(0, bbSize.Y - 0.2, 0), t0 = 0.95 + (i - 1) * 0.12, sway = rng:NextNumber(-1, 1) } notes[i] = m end -- Overlay: one-frame flash at 85% and the banner, high on screen. local gui = Instance.new("ScreenGui") gui.Name = "LabFXOverlay" gui.IgnoreGuiInset = true gui.Parent = game.StarterGui local flash = Instance.new("Frame") flash.Size = UDim2.fromScale(1, 1) flash.BackgroundColor3 = PAPER flash.BackgroundTransparency = 1 flash.BorderSizePixel = 0 flash.Parent = gui local function label(text, size, color, y) local t = Instance.new("TextLabel") t.BackgroundTransparency = 1 t.Text = text t.FontFace = Font.new("rbxasset://fonts/families/Oswald.json", Enum.FontWeight.Bold) t.TextSize = size t.TextColor3 = color t.TextStrokeColor3 = CHAR t.TextStrokeTransparency = 0.3 t.AutomaticSize = Enum.AutomaticSize.XY t.Position = UDim2.new(0.5, 0, y, 0) t.AnchorPoint = Vector2.new(0.5, 0.5) t.TextTransparency = 1 t.Parent = gui return t end local banner = label("PIANO'D", 72, PAPER, 0.11) local sub = label("+150", 34, MINT, 0.185) local DUR = 1.8 local T = 3 / 11 * DUR -- impact lands exactly on capture frame index 3 of 12 local function smooth(x) x = math.clamp(x, 0, 1) return x * x * (3 - 2 * x) end _G.LabFX = { duration = DUR, impact = T, step = function(t) cam.CameraType = Enum.CameraType.Scriptable cam.FieldOfView = 60 local sh = (t >= T and t < T + 0.25) and 0.22 * (1 - (t - T) / 0.25) or 0 cam.CFrame = camCF * CFrame.new(0, sh * math.cos(t * 90), 0) -- small impact shake local u = math.clamp(t / T, 0, 1) local s = t - T -- Piano: ease-in fall from 30 studs with a small roll that rights itself, then bounce and wobble. local y, roll, pitch = restY, 0, 0 if t < T then y = restY + 30 * (1 - u * u) roll = rad(9) * (1 - u) else local b = s / 0.24 if b < 1 then y = restY + 0.4 * math.sin(math.pi * b) end local w = math.exp(-4.5 * s) roll = rad(3) * smooth(s / 0.3) + rad(3.5) * w * math.sin(19 * s) pitch = rad(1.8) * w * math.sin(19 * s + 1.2) end posePiano(CFrame.new(pianoBase.Position + Vector3.new(0, y, 0)) * pianoBase.Rotation * CFrame.Angles(pitch, 0, roll)) -- Shadow grows 0 to 0.4 s, then fades as the real shadow takes over. local g = smooth(t / 0.4) local d = 0.1 + 6.4 * g shadow.Size = Vector3.new(0.04, d, d * 1.25) shadow.CFrame = shadowAt shadow.Transparency = t < T and (1 - 0.4 * math.min(1, t / 0.15)) or math.min(1, 0.6 + s / 0.25 * 0.4) -- Victim: looks up before impact, flattened and dusty after. for _, bp in ipairs(bodyParts) do if t < T then bp.p.Size = bp.size bp.p.Color, bp.p.Material = bp.color, bp.mat if bp.p == head then bp.p.CFrame = neckPivot * CFrame.Angles(rad(48) * smooth(t / 0.3), 0, 0) * neckPivot:ToObjectSpace(bp.cf) else bp.p.CFrame = bp.cf end else local r = bp.rel local pos = Vector3.new(r.Position.X * 1.08, r.Position.Y * 1.05, r.Position.Z * 0.18) bp.p.Size = Vector3.new(bp.size.X * 1.05, bp.size.Y * 1.1, math.max(0.08, bp.size.Z * 0.2)) bp.p.CFrame = lie * CFrame.new(pos) * r.Rotation bp.p.Color = bp.color:Lerp(DUST, 0.45) bp.p.Material = Enum.Material.Concrete end end -- Dust: a burst on impact, thinning over 0.7 s. -- Rate keeps the puff alive between capture calls; the burst only fires on the impact frame. dust.Rate = (s >= 0 and s < 0.8) and (10 + 50 * (1 - s / 0.8)) or 0 if s >= 0 and s < 0.05 then dust:Emit(40) end -- Keys: ballistic from the keyboard end, settle flat on the floor. for _, k in ipairs(keys) do if s < 0 then k.p.Transparency = 1 else local land = (k.v.Y + math.sqrt(k.v.Y ^ 2 + 2 * 40 * (k.from.Y - 0.28))) / 40 local tt = math.min(s, land) local p = k.from + k.v * tt + Vector3.new(0, -20 * tt * tt, 0) k.p.Transparency = 0 if s < land then k.p.CFrame = CFrame.new(p) * CFrame.Angles(k.spin.X * tt, k.spin.Y * tt, k.spin.Z * tt) else k.p.CFrame = CFrame.new(p.X, 0.2 + k.p.Size.Y / 2, p.Z) * CFrame.Angles(0, k.yaw, 0) end end end -- Notes: drift up and fade, turned to the camera. for _, n in ipairs(notes) do local a = (t - n.t0) / (DUR - n.t0) local tr = (a < 0) and 1 or math.clamp(math.max(1 - a * 4, 0) + math.max(0, (a - 0.6) / 0.4), 0, 1) local p = n.at + Vector3.new(0, 2.2 * math.max(a, 0), 0) + side * 0.35 * math.sin(math.max(a, 0) * 5 + n.sway) local f = CFrame.lookAt(p, p + toCam) * CFrame.Angles(0, 0, rad(-18 * n.sway)) n.head.CFrame = f n.stem.CFrame = f * CFrame.new(0.15, 0.3, 0) n.flag.CFrame = f * CFrame.new(0.25, 0.56, 0) * CFrame.Angles(0, 0, rad(-30)) n.head.Transparency, n.stem.Transparency, n.flag.Transparency = tr, tr, tr end flash.BackgroundTransparency = (s >= 0 and s < 0.06) and 0.85 or 1 local pop = math.clamp((s + 0.09) / 0.25, 0, 1) banner.TextTransparency = 1 - pop banner.TextSize = 56 + 16 * pop sub.TextTransparency = 1 - math.clamp((s - 0.15) / 0.25, 0, 1) return t end, cleanup = function() for _, bp in ipairs(bodyParts) do bp.p.Size, bp.p.CFrame, bp.p.Color, bp.p.Material = bp.size, bp.cf, bp.color, bp.mat end local r = workspace:FindFirstChild("LabFX") if r then r:Destroy() end local gg = game.StarterGui:FindFirstChild("LabFXOverlay") if gg then gg:Destroy() end _G.LabFX = nil end, } _G.LabFX.step(0) return { built = #root:GetDescendants(), duration = DUR, impact = T, piano = tostring(bbSize) }