Omni directional Mobility ODM gear movement thingy
Key RequiredUniversal

Omni directional Mobility ODM gear movement thingy

Universal ScriptYeahblxrYeahblxr 1.2K views 1.1K executesUpdated Dec 22, 2025
5.0/ 5(1 votes)
10100% Positive
Lua Script•231 lines (6879 chars)
local UIS = game:GetService("UserInputService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local Workspace = game:GetService("Workspace")

local player = Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local humanoidRootPart = character:WaitForChild("HumanoidRootPart")
local camera = Workspace.CurrentCamera

---------------------------------------------------
-- CONFIGS
---------------------------------------------------
local maxDistance = 5000
local pullStrength = 1        -- rope tension
local boostForce = 70         -- CTRL boost
local idlePull = 20            -- gentle pull toward anchor
local maxSwingSpeed = 200      -- maximum swing speed
local airDrag = 1         -- speed decay
local cableThickness = 0.45
local aimRadius = 25           -- radius for aim assist in studs

-- FOV boost settings
local defaultFOV = camera and camera.FieldOfView or 70
local fovMax = 95              -- maximum FOV at top speed
local fovSpeedStart = 60       -- speed (studs/s) where FOV starts increasing
local fovSpeedMax = 200        -- speed that yields full fovMax
local fovLerpSpeed = 6         -- smoothing factor (higher = faster)

-- Grapple distance multiplier settings
local grappleDistanceStart = 30    -- distance (studs) where extra pull begins
local grappleDistanceMax = maxDistance
local grappleMaxMultiplier = 2.0   -- max multiplier at farthest distance
local grappleMinMultiplier = 1.0   -- multiplier at or below start distance
---------------------------------------------------

local left = { active = false }
local right = { active = false }
local boosting = false

local function createAnchor(position, hitPart)
	local anchor = Instance.new("Part")
	anchor.Size = Vector3.new(0.4, 0.4, 0.4)
	anchor.Anchored = true
	anchor.CanCollide = false
	anchor.Transparency = 1
	anchor.Position = position
	anchor.Parent = Workspace

	if hitPart then
		local weld = Instance.new("WeldConstraint")
		weld.Part0 = anchor
		weld.Part1 = hitPart
		weld.Parent = anchor
	end

	return anchor
end

local function createCable(color)
	local attach0 = Instance.new("Attachment")
	attach0.Position = Vector3.new(0, 0.5, 0)
	attach0.Parent = humanoidRootPart

	local beam = Instance.new("Beam")
	beam.Attachment0 = attach0
	beam.Width0 = cableThickness
	beam.Width1 = cableThickness
	beam.FaceCamera = true
	beam.LightEmission = 0.7
	beam.Color = ColorSequence.new(color)
	beam.Parent = humanoidRootPart

	return beam, attach0
end

local function getMouseHit()
	local mouse = player:GetMouse()
	local ray = camera:ScreenPointToRay(mouse.X, mouse.Y)

	local params = RaycastParams.new()
	params.FilterDescendantsInstances = { character }
	params.FilterType = Enum.RaycastFilterType.Blacklist

	local result = Workspace:Raycast(ray.Origin, ray.Direction * maxDistance, params)
	if result and result.Instance and result.Instance.CanCollide then
		return result.Position, result.Instance
	end

	local closestPart
	local closestPoint
	local closestDist = math.huge
	local rayEnd = ray.Origin + ray.Direction * maxDistance

	for _, part in ipairs(Workspace:GetDescendants()) do
		if part:IsA("BasePart") and part.CanCollide then
			local point = part.Position
			local dist = (point - rayEnd).Magnitude
			if dist < aimRadius and dist < closestDist then
				closestDist = dist
				closestPart = part
				closestPoint = point
			end
		end
	end

	if closestPart then
		return closestPoint, closestPart
	end

	return nil, nil
end

local function fire(side)
	local data = (side == "left") and left or right
	if data.active then return end

	local hitPos, hitPart = getMouseHit()
	if not hitPos or not hitPart then return end

	data.anchor = createAnchor(hitPos, hitPart)
	data.attach1 = Instance.new("Attachment", data.anchor)

	data.beam, data.attach0 = createCable(
		side == "left" and Color3.fromRGB(255, 70, 70)
		or Color3.fromRGB(80, 180, 255)
	)

	data.beam.Attachment1 = data.attach1
	data.length = (humanoidRootPart.Position - hitPos).Magnitude

	-- compute per-grapple pull multiplier based on distance
	local dist = data.length
	local t = 0
	if dist > grappleDistanceStart then
		t = math.clamp((dist - grappleDistanceStart) / math.max(1, (grappleDistanceMax - grappleDistanceStart)), 0, 1)
	end
	data.pullMultiplier = grappleMinMultiplier + (grappleMaxMultiplier - grappleMinMultiplier) * t

	data.active = true
end

local function release(side)
	local data = (side == "left") and left or right
	if not data.active then return end

	data.beam:Destroy()
	data.attach0:Destroy()
	data.attach1:Destroy()
	data.anchor:Destroy()

	data.active = false
end

RunService.Heartbeat:Connect(function(dt)
	for _, data in ipairs({ left, right }) do
		if data.active then
			local hrp = humanoidRootPart
			local toAnchor = data.anchor.Position - hrp.Position
			local distance = toAnchor.Magnitude
			local dir = toAnchor.Unit

			if distance > data.length then
				hrp:ApplyImpulse(dir * (distance - data.length) * pullStrength * (data.pullMultiplier or 1))
			end

			hrp:ApplyImpulse(dir * idlePull * (data.pullMultiplier or 1))

			if boosting then
				hrp:ApplyImpulse(dir * boostForce * (data.pullMultiplier or 1))
			end

			local vel = hrp.AssemblyLinearVelocity
			if vel.Magnitude > maxSwingSpeed then
				hrp.AssemblyLinearVelocity = vel.Unit * maxSwingSpeed
			end

			hrp.AssemblyLinearVelocity *= airDrag
		end
	end

	-- FOV boost based on speed
	local speed = 0
	if humanoidRootPart and humanoidRootPart.AssemblyLinearVelocity then
		speed = humanoidRootPart.AssemblyLinearVelocity.Magnitude
	end

	local t = 0
	if speed > fovSpeedStart then
		t = math.clamp((speed - fovSpeedStart) / math.max(1, (fovSpeedMax - fovSpeedStart)), 0, 1)
	end

	local targetFOV = defaultFOV + (fovMax - defaultFOV) * t
	if camera then
		camera.FieldOfView = camera.FieldOfView + (targetFOV - camera.FieldOfView) * math.clamp(fovLerpSpeed * dt, 0, 1)
	end
end)

UIS.InputBegan:Connect(function(input, gp)
	if gp then return end

	if input.KeyCode == Enum.KeyCode.Q then
		fire("left")
	elseif input.KeyCode == Enum.KeyCode.E then
		fire("right")
	elseif input.KeyCode == Enum.KeyCode.LeftControl or input.KeyCode == Enum.KeyCode.RightControl then
		boosting = true
	end
end)

UIS.InputEnded:Connect(function(input)
	if input.KeyCode == Enum.KeyCode.Q then
		release("left")
	elseif input.KeyCode == Enum.KeyCode.E then
		release("right")
	elseif input.KeyCode == Enum.KeyCode.LeftControl or input.KeyCode == Enum.KeyCode.RightControl then
		boosting = false
	end
end)

player.CharacterAdded:Connect(function(char)
	character = char
	humanoidRootPart = char:WaitForChild("HumanoidRootPart")
	boosting = false
	release("left")
	release("right")
end)

---------------------------------------------------
print("ODM GRAPPLE LOADED")
print("Hold Q/E = Grapple | Hold CTRL = Boost | Aim near objects to auto-snap")

Features & Information

Q = Grapple 1 E = Grapple 2 CTRL = Boost Configs editable to match desire
#Aot#Attack on *an#Odm#Omnidirectional Mobility

Community Comments (2)

UrlocalheckerUrlocalheckerDec 30, 2025

Can u make a mobile version?

Replying to @Urlocalhecker
iluvaotiluvaotSep 2, 2026

@Urlocalhecker i made mobile version check my scripts

More Scripts for Universal Scripts