Загрузка данных
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
-- ================= НАСТРОЙКИ =================
local Settings = {
ToolName = "Самокат",
HoverHeight = 0.5, -- высота центра самоката над землёй (как в клиентском скрипте)
SeatHeight = 2.0, -- высота невидимого сиденья (если ноги в деке или парят, меняй это)
BarspinTime = 0.45,
TailwhipTime = 0.6,
HandsOnBars = true, -- руки на руле (только R15)
}
-- =============================================
local DARK = Color3.fromRGB(35, 35, 40)
local ACCENT = Color3.fromRGB(230, 60, 60)
local trickEvent = Instance.new("RemoteEvent")
trickEvent.Name = "ScooterTrick"
trickEvent.Parent = ReplicatedStorage
local rides = {}
local function mk(parent, name, size, color)
local p = Instance.new("Part")
p.Name = name
p.Size = size
p.Color = color
p.Material = Enum.Material.Metal
p.CanCollide = false
p.CanQuery = false
p.CanTouch = false
p.Massless = true
p.TopSurface = Enum.SurfaceType.Smooth
p.BottomSurface = Enum.SurfaceType.Smooth
p.Parent = parent
return p
end
local function weld(a, b)
local w = Instance.new("Weld")
w.Part0 = a
w.Part1 = b
w.C0 = a.CFrame:ToObjectSpace(b.CFrame)
w.Parent = b
return w
end
local function build(cf)
local model = Instance.new("Model")
model.Name = "ScooterRide"
local core = mk(model, "Core", Vector3.new(1.2, 0.4, 3.2), DARK)
core.Transparency = 1
core.CanCollide = true
core.Massless = false
core.CustomPhysicalProperties = PhysicalProperties.new(1, 0, 0, 1, 1)
core.CFrame = cf
local deck = mk(model, "Deck", Vector3.new(1.4, 0.25, 3.6), DARK)
deck.CFrame = cf * CFrame.new(0, 0.05, 0)
local wheelF = mk(model, "WheelF", Vector3.new(0.25, 0.7, 0.7), ACCENT)
wheelF.Shape = Enum.PartType.Cylinder
wheelF.CFrame = cf * CFrame.new(0, -0.15, -1.55)
local wheelB = mk(model, "WheelB", Vector3.new(0.25, 0.7, 0.7), ACCENT)
wheelB.Shape = Enum.PartType.Cylinder
wheelB.CFrame = cf * CFrame.new(0, -0.15, 1.55)
local column = mk(model, "Column", Vector3.new(0.25, 2.9, 0.25), DARK)
column.CFrame = cf * CFrame.new(0, 1.65, -1.35)
local bar = mk(model, "Handlebar", Vector3.new(1.5, 0.22, 0.22), ACCENT)
bar.CFrame = cf * CFrame.new(0, 3.05, -1.35)
-- точки, куда кладутся руки
local gripL = Instance.new("Attachment")
gripL.Name = "GripL"
gripL.Position = Vector3.new(-0.6, 0, 0)
gripL.Parent = bar
local gripR = Instance.new("Attachment")
gripR.Name = "GripR"
gripR.Position = Vector3.new(0.6, 0, 0)
gripR.Parent = bar
local seat = Instance.new("VehicleSeat")
seat.Name = "Seat"
seat.Size = Vector3.new(1, 0.3, 1.2)
seat.Transparency = 1
seat.CanCollide = false
seat.CanQuery = false
seat.CanTouch = false
seat.Massless = true
seat.MaxSpeed = 0
seat.Torque = 0
seat.TurnSpeed = 0
seat.HeadsUpDisplay = false
seat.CFrame = cf * CFrame.new(0, Settings.SeatHeight, 0)
seat:SetAttribute("IsScooter", true)
seat.Parent = model
local deckWeld = weld(core, deck)
local wfWeld = weld(deck, wheelF)
local wbWeld = weld(deck, wheelB)
local barWeld = weld(core, column)
weld(column, bar)
weld(core, seat)
model.PrimaryPart = core
return {
model = model, core = core, seat = seat, bar = bar,
deckWeld = deckWeld, deckBase = deckWeld.C0,
barWeld = barWeld, barBase = barWeld.C0,
wfWeld = wfWeld, wfBase = wfWeld.C0,
wbWeld = wbWeld, wbBase = wbWeld.C0,
wheelAngle = 0, busy = {}, iks = {},
}
end
local function stopRide(plr)
local r = rides[plr]
if not r then return end
rides[plr] = nil
for _, ik in ipairs(r.iks) do
ik:Destroy()
end
if r.model then r.model:Destroy() end
end
-- руки на руле (IK, только R15)
local function addHands(r, hum, char)
if not Settings.HandsOnBars then return end
if hum.RigType ~= Enum.HumanoidRigType.R15 then return end
local defs = {
{ "RightUpperArm", "RightHand", "GripR" },
{ "LeftUpperArm", "LeftHand", "GripL" },
}
for _, d in ipairs(defs) do
local chainRoot = char:FindFirstChild(d[1])
local effector = char:FindFirstChild(d[2])
local target = r.bar:FindFirstChild(d[3])
if chainRoot and effector and target then
local ik = Instance.new("IKControl")
ik.Name = "ScooterIK_" .. d[3]
ik.ChainRoot = chainRoot
ik.EndEffector = effector
ik.Target = target
ik.Type = Enum.IKControlType.Position
ik.Weight = 1
ik.SmoothTime = 0.05
ik.Parent = hum
table.insert(r.iks, ik)
end
end
end
local function startRide(plr, tool)
stopRide(plr)
local char = tool.Parent
if not char or not char:IsA("Model") then return end
local hum = char:FindFirstChildOfClass("Humanoid")
local root = char:FindFirstChild("HumanoidRootPart")
if not hum or not root or hum.Health <= 0 then return end
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = { char }
local hit = workspace:Raycast(root.Position, Vector3.new(0, -30, 0), params)
local groundY = hit and hit.Position.Y or (root.Position.Y - 3)
local look = root.CFrame.LookVector
local flat = Vector3.new(look.X, 0, look.Z)
if flat.Magnitude < 0.01 then flat = Vector3.new(0, 0, -1) end
flat = flat.Unit
local pos = Vector3.new(root.Position.X, groundY + Settings.HoverHeight + 0.1, root.Position.Z)
local cf = CFrame.lookAt(pos, pos + flat)
local r = build(cf)
r.model.Parent = workspace
rides[plr] = r
pcall(function() r.core:SetNetworkOwner(plr) end)
r.seat:Sit(hum)
addHands(r, hum, char)
end
-- визуальные трюки (на сервере, чтобы видели все)
local function animate(duration, setter)
local t = 0
while t < duration do
t += RunService.Heartbeat:Wait()
local a = math.clamp(t / duration, 0, 1)
a = a * a * (3 - 2 * a)
setter(a * 2 * math.pi)
end
setter(0)
end
trickEvent.OnServerEvent:Connect(function(plr, name)
local r = rides[plr]
if not r or r.busy[name] then return end
if name == "Barspin" then
r.busy[name] = true
task.spawn(function()
animate(Settings.BarspinTime, function(a)
r.barWeld.C0 = r.barBase * CFrame.Angles(0, a, 0)
end)
r.busy[name] = nil
end)
elseif name == "Tailwhip" then
r.busy[name] = true
task.spawn(function()
animate(Settings.TailwhipTime, function(a)
r.deckWeld.C0 = r.deckBase * CFrame.Angles(0, a, 0)
end)
r.busy[name] = nil
end)
end
end)
-- вращение колёс по скорости
RunService.Heartbeat:Connect(function(dt)
for _, r in pairs(rides) do
local core = r.core
if core and core.Parent then
local speed = core.AssemblyLinearVelocity:Dot(core.CFrame.LookVector)
r.wheelAngle += speed / 0.35 * dt
local rot = CFrame.Angles(-r.wheelAngle, 0, 0)
r.wfWeld.C0 = r.wfBase * rot
r.wbWeld.C0 = r.wbBase * rot
end
end
end)
-- предмет "Самокат"
local function makeTool()
local tool = Instance.new("Tool")
tool.Name = Settings.ToolName
tool.RequiresHandle = true
tool.CanBeDropped = false
tool.ToolTip = "W/S газ, A/D поворот, Пробел прыжок, V вили, Q/E/R/T трюки, C слезть"
local handle = Instance.new("Part")
handle.Name = "Handle"
handle.Size = Vector3.new(0.4, 0.4, 0.4)
handle.Transparency = 1
handle.CanCollide = false
handle.Massless = true
handle.Parent = tool
return tool
end
local function giveTool(plr)
local bp = plr:WaitForChild("Backpack")
task.wait(0.2)
if bp:FindFirstChild(Settings.ToolName) or (plr.Character and plr.Character:FindFirstChild(Settings.ToolName)) then
return
end
local tool = makeTool()
tool.Equipped:Connect(function() startRide(plr, tool) end)
tool.Unequipped:Connect(function() stopRide(plr) end)
tool.Parent = bp
end
local function onPlayer(plr)
plr.CharacterAdded:Connect(function(char)
stopRide(plr)
giveTool(plr)
local hum = char:WaitForChild("Humanoid")
hum.Died:Connect(function() stopRide(plr) end)
end)
plr.CharacterRemoving:Connect(function() stopRide(plr) end)
if plr.Character then task.spawn(giveTool, plr) end
end
Players.PlayerAdded:Connect(onPlayer)
for _, plr in ipairs(Players:GetPlayers()) do onPlayer(plr) end
Players.PlayerRemoving:Connect(function(plr) stopRide(plr) end)