Upgraded Rocket launcher for NPC ONLY FE credits by DuaKNo
Key RequiredUniversal

Upgraded Rocket launcher for NPC ONLY FE credits by DuaKNo

Universal ScriptKinitopet890bloxHackKinitopet890bloxHack 479 views 1.6K executesUpdated Sep 30, 2026
Unrated
00
Lua Script•988 lines (28708 chars)
--[[
	CLASSIC ROCKET LAUNCHER - Client-only (Explosion visual kept, engine kill disabled)
]]

--========================================================
-- SIMULATION RADIUS BOOST
--========================================================
local LocalPlayer = game:GetService("Players").LocalPlayer

local function boostRadius()
	pcall(function() sethiddenproperty(LocalPlayer, "SimulationRadius", 5000) end)
	pcall(function() setsimulationradius(5000, 5000) end)
end

boostRadius()

task.spawn(function()
	while true do
		task.wait(2)
		boostRadius()
	end
end)

--========================================================
-- ROCKET LAUNCHER
--========================================================
local function runDummyScript(f, scri)
	local oldenv = getfenv(f)
	local newenv = setmetatable({}, {
		__index = function(_, k)
			if k:lower() == 'script' then return scri else return oldenv[k] end
		end
	})
	setfenv(f, newenv)
	ypcall(function() f() end)
end

local cors = {}

local mas = Instance.new("Model", game:GetService("Lighting"))
mas.Name = "CompiledModel"

local Tool          = Instance.new("Tool")
local Handle        = Instance.new("Part")
local Mesh          = Instance.new("SpecialMesh")
local ReloadSound   = Instance.new("Sound")
local ToolScript    = Instance.new("LocalScript")
local MouseIcon     = Instance.new("LocalScript")
local ShuHold       = Instance.new("Script")

Tool.Name = "Rocket Launcher"
Tool.Parent = mas
Tool.TextureId = ""
Tool.Grip = CFrame.new(0.9, -0.01, -0.4) * CFrame.fromOrientation(0, math.rad(-90), math.rad(-90))
Tool.CanBeDropped = false

Handle.Name = "Handle"
Handle.Parent = Tool
Handle.Position = Vector3.new(787.34, 3.70, 228.68)
Handle.Rotation = Vector3.new(-180, 0, -180)
Handle.CanCollide = false
Handle.FormFactor = Enum.FormFactor.Custom
Handle.Size = Vector3.new(5, 1, 1)
Handle.CFrame = CFrame.new(787.34, 3.70, 228.68, -1, 0, 0, 0, 1, 0, 0, 0, -1)
Handle.BottomSurface = Enum.SurfaceType.Smooth
Handle.TopSurface = Enum.SurfaceType.Smooth

Mesh.Parent = Handle
Mesh.MeshId = "rbxassetid://12221651"
Mesh.TextureId = "rbxassetid://12224070"
Mesh.Scale = Vector3.new(1, 1, 1)
Mesh.MeshType = Enum.MeshType.FileMesh

ReloadSound.Name = "Reload"
ReloadSound.Parent = Handle
ReloadSound.SoundId = "rbxassetid://5801258866"
ReloadSound.Volume = 1.2
ReloadSound.Pitch = 0.9
ReloadSound.RollOffMaxDistance = 250
ReloadSound.RollOffMinDistance = 5
ReloadSound.RollOffMode = Enum.RollOffMode.InverseTapered

ToolScript.Name = "ToolScript"
ToolScript.Parent = Tool
table.insert(cors, coroutine.create(function()
	wait()
	runDummyScript(function()
		local SHOT_SPEED = 180
		local SHOT_TIME  = 10
		local MUZZLE_LOCAL_OFFSET = Vector3.new(2.2, 0, 0)
		local EXPLOSION_RADIUS = 12
		local EXPLOSION_SOUND_ID = "rbxassetid://12221984"

		local ROCKET_FORWARD_OFFSET = 1.5
		local MAX_CLICK_RANGE = 500

		local ROCKET_MESH_ID = "rbxassetid://2251534"
		local ROCKET_MESH_SCALE = Vector3.new(0.5, 0.5, 0.5)
		local ROCKET_COLOR = "Medium stone grey"
		local ROCKET_FIRE_HEAT = 4
		local ROCKET_FIRE_SIZE = 2

		local ROCKET_INITIAL_ARC   = 0.45
		local ROCKET_STEER_P       = 2500
		local ROCKET_STEER_D       = 250
		local ROCKET_DETONATE_DIST = 3.0
		local ROCKET_ARC_MIN_DIST  = 25
		local ROCKET_ARC_FULL_DIST = 90
		local ROCKET_ARM_TIME      = 0.15

		local ROCKET_SLOWDOWN_DIST = 25
		local ROCKET_MIN_SPEED_FRACTION = 0.35
		local ROCKET_CORNER_MIN_SPEED = 0.35

		local WHOOSH_START_VOLUME = 2
		local WHOOSH_FADE_START   = 15
		local WHOOSH_FADE_END     = 220

		-- === OBSTACLE AVOIDANCE ===
		local ROCKET_OBSTACLE_LOOKAHEAD   = 100
		local ROCKET_OBSTACLE_AVOID_PULL  = 3.2
		local ROCKET_AVOID_BLEND          = 0.85
		local ROCKET_AVOID_STICKY_TIME    = 0.60
		local ROCKET_AVOID_RAMP_EXP       = 0.45

		local ROCKET_MAX_LEAD_TIME = 0.75
		local ROCKET_MIN_LEAD_SPEED = 2

		local ROCKET_TERMINAL_LOCK_DIST = 30
		local ROCKET_OVERSHOOT_DIST = 12

		-- === EXPLOSION TUNING ===
		local EXPLOSION_BLAST_PRESSURE   = 0     -- Keeps explosion visual & instance active, but strips out default engine kill force
		local BLAST_KNOCKBACK_STRENGTH   = 250   
		local NPC_KNOCKBACK_STRENGTH     = 110   

		local PlayersService     = Game:GetService('Players')
		local DebrisService      = Game:GetService('Debris')
		local RunService         = Game:GetService('RunService')
		local SoundService       = Game:GetService('SoundService')
		local UserInputService   = Game:GetService('UserInputService')

		local Workspace = workspace
		local LocalPlayer = PlayersService.LocalPlayer

		local Tool = script.Parent
		local Handle = Tool:WaitForChild('Handle')
		local ReloadSound = Handle:WaitForChild('Reload')

		local Character, Humanoid, Player = nil, nil, nil
		local BaseShot = nil

		local ActiveShot     = nil
		local ActiveWhoosh   = nil
		local ActiveUpdateConn = nil

		local function cleanupActive()
			if ActiveUpdateConn then
				ActiveUpdateConn:Disconnect()
				ActiveUpdateConn = nil
			end
			if ActiveWhoosh then
				ActiveWhoosh:Stop()
				ActiveWhoosh:Destroy()
				ActiveWhoosh = nil
			end
			if ActiveShot then
				ActiveShot = nil
			end
		end

		local function playAtPosition(soundId, position, volume, pitch)
			local anchor = Instance.new("Part")
			anchor.Anchored = true
			anchor.CanCollide = false
			anchor.Transparency = 1
			anchor.Size = Vector3.new(0.1, 0.1, 0.1)
			anchor.CFrame = CFrame.new(position)
			anchor.Parent = Workspace

			local s = Instance.new("Sound")
			s.SoundId = soundId
			s.Volume = volume or 2
			s.Pitch = pitch or 1
			s.RollOffMaxDistance = 500
			s.RollOffMinDistance = 5
			s.RollOffMode = Enum.RollOffMode.InverseTapered
			s.Parent = anchor

			s:Play()
			DebrisService:AddItem(anchor, 5)
		end

		local function WeOwnCharacter(character)
			if not character or not character.Parent then return false end
			local root = character:FindFirstChild("HumanoidRootPart")
				or character.PrimaryPart
			if not root then return false end
			local owner = nil
			pcall(function() owner = root:GetNetworkOwner() end)
			return owner == LocalPlayer
		end

		local function TryTakeOwnership(character)
			if not character or not character.Parent then return false end
			local gotAny = false
			for _, part in ipairs(character:GetDescendants()) do
				if part:IsA("BasePart") then
					local ok = pcall(function()
						part:SetNetworkOwner(LocalPlayer)
					end)
					if ok then gotAny = true end
				end
			end
			return gotAny
		end

		local function ApplyDamageStack(humanoid, damage)
			if not humanoid or not humanoid.Parent then return end
			if damage and damage > 0 then
				pcall(function() humanoid:TakeDamage(damage) end)
			end
			if not damage then
				pcall(function() humanoid.Health = -1 end)
				pcall(function() humanoid:TakeDamage(9999999999) end)
			elseif humanoid.Health > 0 and damage >= 9999999 then
				pcall(function() humanoid.Health = -1 end)
				pcall(function() humanoid:TakeDamage(9999999999) end)
			end
		end

		local function ReliableKill(character, humanoid)
			task.spawn(function()
				ApplyDamageStack(humanoid)

				if humanoid.Health <= 0 then return end

				for _ = 1, 20 do
					if not character.Parent or not humanoid.Parent then return end
					if humanoid.Health <= 0 then return end

					if not WeOwnCharacter(character) then
						TryTakeOwnership(character)
					end
					ApplyDamageStack(humanoid)
					task.wait(0.1)
				end
			end)
		end

		local EXPLOSION_DAMAGE_BANDS = {
			{0.50, 9999999},
			{0.58, 0.90},
			{0.67, 0.70},
			{0.75, 0.50},
			{0.83, 0.35},
			{0.92, 0.20},
			{1.00, 0.10},
		}

		local function damageFractionForRatio(ratio)
			for _, band in ipairs(EXPLOSION_DAMAGE_BANDS) do
				if ratio <= band[1] then
					return band[2]
				end
			end
			return 0
		end

		local function isPlayerCharacter(char)
			if not char then return false end
			local ok, plr = pcall(function()
				return PlayersService:GetPlayerFromCharacter(char)
			end)
			return ok and plr ~= nil
		end

		local overlapParams = OverlapParams.new()
		overlapParams.FilterType = Enum.RaycastFilterType.Exclude
		overlapParams.FilterDescendantsInstances = {}

		local function applyExplosion(pos)
			local radius   = EXPLOSION_RADIUS
			local propKb   = BLAST_KNOCKBACK_STRENGTH
			local npcKb    = NPC_KNOCKBACK_STRENGTH

			local parts = Workspace:GetPartBoundsInRadius(pos, radius, overlapParams)
			local seenChars = {}

			for _, part in ipairs(parts) do
				local char = nil
				local node = part
				for _ = 1, 4 do
					if not node or node == Workspace then break end
					node = node.Parent
					if node and node:IsA("Model")
						and node:FindFirstChildOfClass("Humanoid") then
						char = node
						break
					end
				end

				if char then
					if not seenChars[char] then
						seenChars[char] = true

						local hum  = char:FindFirstChildOfClass("Humanoid")
						local root = char:FindFirstChild("HumanoidRootPart")

						if hum and root and hum.Health > 0
							and char ~= Character
							and not isPlayerCharacter(char) then

							local d = (root.Position - pos).Magnitude
							if d <= radius then

								if not root.Anchored then
									local delta = root.Position - pos
									local dir
									if delta.Magnitude < 0.01 then
										dir = Vector3.new(0, 1, 0)
									else
										dir = delta.Unit
									end
									dir = (dir + Vector3.new(0, 0.55, 0)).Unit

									local falloff = 1 - (d / radius)
									local mass = root:GetMass()
									local impulse = dir * (npcKb * falloff) * mass

									pcall(function()
										root:ApplyImpulse(impulse)
									end)
								end

								local frac = damageFractionForRatio(d / radius)

								if frac > 1 then
									ReliableKill(char, hum)
								else
									local damage =
										(hum.MaxHealth > 0 and hum.MaxHealth or 100) * frac

									task.spawn(function()
										if not WeOwnCharacter(char) then
											for _ = 1, 5 do
												if not char.Parent or not hum.Parent
													or hum.Health <= 0 then return end
												TryTakeOwnership(char)
												if WeOwnCharacter(char) then break end
												task.wait(0.05)
											end
										end

										if not char.Parent or not hum.Parent then return end
										if hum.Health <= 0 then return end

										ApplyDamageStack(hum, damage)
									end)
								end
							end
						end
					end
				elseif part:IsA("BasePart") and not part.Anchored then
					local delta = part.Position - pos
					local d = delta.Magnitude
					if d <= radius and d > 0.01 then
						local falloff = 1 - (d / radius)
						local mass = part:GetMass()
						local impulse = delta.Unit * (propKb * falloff) * mass
						pcall(function() part:ApplyImpulse(impulse) end)
					end
				end
			end
		end

		local function killSounds(part)
			if not part then return end
			for _, child in ipairs(part:GetChildren()) do
				if child:IsA("Sound") then
					child.Looped = false
					child:Stop()
					child:Destroy()
				end
			end
		end

		local shotTargets = setmetatable({}, {__mode = "k"})

		local function OnTouched(shot, otherPart, detonatePosition)
			if not shot or not shot.Parent then return end
			if shot:GetAttribute("Hit") then return end

			if otherPart then
				if not shot:GetAttribute("Armed") then
					local tgt = shotTargets[shot]
					if not (tgt and otherPart:IsDescendantOf(tgt)) then
						return
					end
				end
				local allowSelf = shot:GetAttribute("AllowSelfHit") == true
				if (not allowSelf) and Character and otherPart:IsDescendantOf(Character) then
					return
				end
			end

			shot:SetAttribute("Hit", true)

			local pos = detonatePosition or shot.Position

			cleanupActive()
			killSounds(shot)
			shot:Destroy()

			-- Spawns the standard visual explosion object, but with 0 blast pressure so your custom damage bands handle the kills safely
			local explosion = Instance.new("Explosion")
			explosion.BlastRadius = EXPLOSION_RADIUS
			explosion.BlastPressure = EXPLOSION_BLAST_PRESSURE
			explosion.DestroyJointRadiusPercent = 0
			explosion.Position = pos
			explosion.Parent = Workspace

			applyExplosion(pos)

			playAtPosition(EXPLOSION_SOUND_ID, pos, 2.5, 1)
		end

		local function OnEquipped()
			Character = Tool.Parent
			Humanoid = Character:WaitForChild('Humanoid')
			Player = PlayersService:GetPlayerFromCharacter(Character)
		end

		local function nozzleCFrame(position, forward)
			forward = forward.Magnitude > 1e-4 and forward.Unit or Vector3.new(0, 0, -1)

			local upRef = Vector3.new(0, 1, 0)
			if math.abs(forward:Dot(upRef)) > 0.99 then
				upRef = Vector3.new(0, 0, 1)
			end

			local vZ = -forward
			local vY = (upRef - vZ * vZ:Dot(upRef)).Unit
			local vX = vY:Cross(vZ)
			return CFrame.fromMatrix(position, vX, vY, vZ)
		end

		local function closestPointOnSegment(A, B, P)
			local ab = B - A
			local abLen2 = ab:Dot(ab)
			if abLen2 < 1e-6 then
				return (P - A).Magnitude, A
			end
			local t = (P - A):Dot(ab) / abLen2
			t = math.clamp(t, 0, 1)
			local closest = A + ab * t
			return (P - closest).Magnitude, closest
		end

		local function findClickTarget()
			local camera = Workspace.CurrentCamera
			if not camera then
				return { staticPosition = Humanoid and Humanoid.TargetPoint or Vector3.new() }
			end

			local mouseLocation = UserInputService:GetMouseLocation()
			local ray = camera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)

			local params = RaycastParams.new()
			params.FilterType = Enum.RaycastFilterType.Exclude
			local excludeList = {}
			if Handle then table.insert(excludeList, Handle) end
			if ActiveShot then table.insert(excludeList, ActiveShot) end
			params.FilterDescendantsInstances = excludeList

			local result = Workspace:Raycast(ray.Origin, ray.Direction * MAX_CLICK_RANGE, params)

			if not result then
				local fallback = Humanoid and Humanoid.TargetPoint or (ray.Origin + ray.Direction * 100)
				return { staticPosition = fallback }
			end

			local hitPart = result.Instance

			local currentModel = hitPart:FindFirstAncestorOfClass("Model")
			while currentModel do
				local modelHumanoid = currentModel:FindFirstChildOfClass("Humanoid")
				local rootPart = currentModel:FindFirstChild("HumanoidRootPart")
					or (modelHumanoid and modelHumanoid.RootPart)
					or (modelHumanoid and modelHumanoid.Torso)

				if modelHumanoid or rootPart then
					return {
						npc            = currentModel,
						humanoid       = modelHumanoid,
						rootPart       = rootPart or hitPart,
						hitPosition    = result.Position,
						staticPosition = result.Position,
					}
				end

				currentModel = currentModel:FindFirstAncestorOfClass("Model")
			end

			return {
				staticPosition = result.Position,
				hitPart        = hitPart,
			}
		end

		local function shouldDodgeObstacle(hitPart)
			local node = hitPart
			local topModel = nil
			for _ = 1, 6 do
				if not node or node == Workspace then break end
				if node:IsA("Model") then
					if node:FindFirstChildOfClass("Humanoid") then
						return false
					end
					topModel = node
				end
				node = node.Parent
			end

			local size
			if topModel then
				local ok, bbox = pcall(function()
					return topModel:GetExtentsSize()
				end)
				if ok and bbox then
					size = bbox
				end
			end
			if not size then
				size = hitPart.Size
			end

			local axes = {size.X, size.Y, size.Z}
			table.sort(axes)
			if axes[1] < 3 and axes[2] < 3 then
				return false
			end

			return true
		end

		local function findAvoidanceDir(origin, desiredDir, params, maxLen)
			local lookLen = math.min(ROCKET_OBSTACLE_LOOKAHEAD, maxLen or ROCKET_OBSTACLE_LOOKAHEAD)

			local hit = Workspace:Raycast(origin, desiredDir * lookLen, params)
			if not hit then return nil, 0 end

			if not shouldDodgeObstacle(hit.Instance) then
				return nil, 0
			end

			local hitDist = (hit.Position - origin).Magnitude
			local strength = math.clamp(1 - (hitDist / lookLen), 0, 1)
			strength = strength ^ ROCKET_AVOID_RAMP_EXP

			local up = Vector3.new(0, 1, 0)
			if math.abs(desiredDir:Dot(up)) > 0.95 then
				up = Vector3.new(0, 0, 1)
			end
			local right = desiredDir:Cross(up).Unit
			local left = -right
			local down = -up

			local candidates = {
				right, left,
				(right + up).Unit,   (left + up).Unit,
				(right + down).Unit, (left + down).Unit,
				up, down,
			}
			local bestDir, bestScore = nil, -math.huge

			for _, side in ipairs(candidates) do
				local candidate = (desiredDir + side * ROCKET_OBSTACLE_AVOID_PULL).Unit
				local test = Workspace:Raycast(origin, candidate * lookLen, params)
				local clearance
				if test then
					clearance = (test.Position - origin).Magnitude
				else
					clearance = lookLen
				end
				local score = clearance + candidate:Dot(desiredDir) * 6
				if candidate.Y < -0.3 then
					score = score - 8
				end
				if score > bestScore then
					bestScore, bestDir = score, candidate
				end
			end

			return bestDir, strength
		end

		local function OnActivated()
			if Tool.Enabled and Humanoid.Health > 0 then
				Tool.Enabled = false

				cleanupActive()

				local handleCFrame = Handle.CFrame
				local firingPoint = handleCFrame:PointToWorldSpace(MUZZLE_LOCAL_OFFSET)

				local clickTarget = findClickTarget()

				local initialTargetPos
				if clickTarget.npc and clickTarget.rootPart and clickTarget.rootPart.Parent then
					initialTargetPos = clickTarget.rootPart.Position
				else
					initialTargetPos = clickTarget.staticPosition
				end

				local aimVec = initialTargetPos - firingPoint
				if aimVec.Magnitude < 1e-3 then
					aimVec = handleCFrame.lookVector * 100
					initialTargetPos = firingPoint + aimVec
				end

				local targetDistance = (initialTargetPos - firingPoint).Magnitude

				local arcAmount = 0
				if targetDistance > ROCKET_ARC_MIN_DIST then
					local t = math.clamp(
						(targetDistance - ROCKET_ARC_MIN_DIST) /
						(ROCKET_ARC_FULL_DIST - ROCKET_ARC_MIN_DIST),
						0, 1)
					arcAmount = ROCKET_INITIAL_ARC * t
				end

				local initialForward = (aimVec.Unit + Vector3.new(0, arcAmount, 0)).Unit

				local shot = BaseShot:Clone()
				local initialCF = nozzleCFrame(firingPoint + initialForward * ROCKET_FORWARD_OFFSET, initialForward)
				shot.CFrame = initialCF
				ActiveShot = shot

				if clickTarget.npc then
					shotTargets[shot] = clickTarget.npc
				end
				task.delay(ROCKET_ARM_TIME, function()
					if shot and shot.Parent then
						shot:SetAttribute("Armed", true)
					end
				end)

				local isSelfTarget = (clickTarget.npc ~= nil and clickTarget.npc == Character)
				if isSelfTarget then
					shot:SetAttribute("AllowSelfHit", true)
				end

				local targetPart = nil
				if clickTarget.npc and clickTarget.rootPart and clickTarget.rootPart.Parent then
					targetPart = clickTarget.rootPart
				end

				local whoosh = Instance.new("Sound")
				whoosh.Name = "RocketWhoosh"
				whoosh.SoundId = "rbxasset://sounds/Rocket whoosh 01.wav"
				whoosh.Looped = true
				whoosh.Volume = WHOOSH_START_VOLUME
				whoosh.Parent = SoundService
				ActiveWhoosh = whoosh

				local gyro = Instance.new("BodyGyro")
				gyro.MaxTorque = Vector3.new(math.huge, math.huge, math.huge)
				gyro.P = ROCKET_STEER_P
				gyro.D = ROCKET_STEER_D
				gyro.CFrame = initialCF
				gyro.Parent = shot

				local bodyVelocity = Instance.new("BodyVelocity")
				bodyVelocity.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
				bodyVelocity.Velocity = initialForward * SHOT_SPEED
				bodyVelocity.Parent = shot

				local lastPos = shot.Position
				local committedAvoidDir, committedAvoidUntil = nil, 0
				local terminalLock = false

				local ignoreList = {shot, Handle}
				if Character then table.insert(ignoreList, Character) end
				if clickTarget.npc then table.insert(ignoreList, clickTarget.npc) end

				local avoidParams = RaycastParams.new()
				avoidParams.FilterType = Enum.RaycastFilterType.Exclude
				avoidParams.FilterDescendantsInstances = ignoreList
				avoidParams.IgnoreWater = true

				local updateConn
				updateConn = RunService.Heartbeat:Connect(function()
					if not shot or not shot.Parent then
						if updateConn then updateConn:Disconnect() end
						return
					end

					local liveTargetPos
					if targetPart and targetPart.Parent then
						liveTargetPos = targetPart.Position
					else
						liveTargetPos = clickTarget.staticPosition
					end

					local currentPos = shot.Position
					local distToTarget = (liveTargetPos - currentPos).Magnitude

					local closestDist, closestPt = closestPointOnSegment(lastPos, currentPos, liveTargetPos)
					if closestDist <= ROCKET_DETONATE_DIST then
						OnTouched(shot, nil, closestPt)
						return
					end

					if not terminalLock and distToTarget <= ROCKET_TERMINAL_LOCK_DIST then
						terminalLock = true
					end

					if terminalLock then
						local lockVec = liveTargetPos - currentPos
						if lockVec.Magnitude > 0.01 then
							local dir = lockVec.Unit

							local forwardDot = dir:Dot(shot.CFrame.LookVector)
							if forwardDot < -0.5 and distToTarget < ROCKET_OVERSHOOT_DIST then
								OnTouched(shot, nil, currentPos)
								return
							end

							gyro.CFrame = CFrame.new(currentPos, currentPos + dir)
							bodyVelocity.Velocity = dir * SHOT_SPEED
						else
							OnTouched(shot, nil, currentPos)
							return
						end
					else
						local steerTargetPos = liveTargetPos
						local toTargetSteer = steerTargetPos - currentPos
						local finalDir = shot.CFrame.LookVector

						if toTargetSteer.Magnitude > 0.5 then
							local desiredDir = toTargetSteer.Unit
							finalDir = desiredDir

							local probeLen = math.min(
								ROCKET_OBSTACLE_LOOKAHEAD,
								math.max(0, distToTarget - 4))

							if probeLen >= 5 then
								local blend = 0

								if committedAvoidDir and os.clock() < committedAvoidUntil then
									local probe = Workspace:Raycast(
										currentPos,
										desiredDir * probeLen,
										avoidParams)
									if probe and shouldDodgeObstacle(probe.Instance) then
										local d = (probe.Position - currentPos).Magnitude
										local s = math.clamp(1 - (d / probeLen), 0, 1)
										blend = ROCKET_AVOID_BLEND * (s ^ ROCKET_AVOID_RAMP_EXP)
									elseif probe then
										committedAvoidDir = nil
										blend = 0
									else
										blend = ROCKET_AVOID_BLEND
									end
									if blend > 0 then
										finalDir = (desiredDir * (1 - blend)
											+ committedAvoidDir * blend).Unit
									end
								else
									local avoidDir, avoidStrength =
										findAvoidanceDir(currentPos, desiredDir, avoidParams, probeLen)
									if avoidDir then
										committedAvoidDir = avoidDir
										committedAvoidUntil = os.clock() + ROCKET_AVOID_STICKY_TIME
										blend = ROCKET_AVOID_BLEND * avoidStrength
										finalDir = (desiredDir * (1 - blend)
											+ avoidDir * blend).Unit
									else
										committedAvoidDir = nil
									end
								end
							else
								committedAvoidDir = nil
							end

							gyro.CFrame = CFrame.new(currentPos, currentPos + finalDir)
						end

						local speed = SHOT_SPEED
						if distToTarget < ROCKET_SLOWDOWN_DIST then
							local t = distToTarget / ROCKET_SLOWDOWN_DIST
							speed = SHOT_SPEED * math.max(ROCKET_MIN_SPEED_FRACTION, t)
						end

						local curForward = shot.CFrame.LookVector
						local alignment = math.clamp(curForward:Dot(finalDir), 0, 1)
						local cornerFactor = ROCKET_CORNER_MIN_SPEED
							+ (1 - ROCKET_CORNER_MIN_SPEED) * alignment
						speed = speed * cornerFactor

						bodyVelocity.Velocity = curForward * speed
					end

					lastPos = currentPos

					if whoosh and whoosh.Parent then
						local cam = Workspace.CurrentCamera
						if cam then
							local dist = (currentPos - cam.CFrame.Position).Magnitude
							local alpha
							if dist <= WHOOSH_FADE_START then
								alpha = 1
							elseif dist >= WHOOSH_FADE_END then
								alpha = 0
							else
								alpha = 1 - ((dist - WHOOSH_FADE_START) / (WHOOSH_FADE_END - WHOOSH_FADE_START))
							end
							whoosh.Volume = WHOOSH_START_VOLUME * alpha
							if alpha <= 0 then
								whoosh:Stop()
							end
						end
					end
				end)
				ActiveUpdateConn = updateConn

				shot.Destroying:Connect(function()
					if updateConn then updateConn:Disconnect() end
					if ActiveUpdateConn == updateConn then ActiveUpdateConn = nil end
					if whoosh then
						whoosh:Stop()
						whoosh:Destroy()
						if ActiveWhoosh == whoosh then ActiveWhoosh = nil end
					end
				end)

				whoosh:Play()

				shot.Touched:Connect(function(otherPart)
					OnTouched(shot, otherPart)
				end)
				DebrisService:AddItem(shot, SHOT_TIME)
				shot.Parent = Tool

				task.delay(SHOT_TIME + 0.1, function()
					if ActiveShot == shot then
						cleanupActive()
					end
				end)

				wait(0)
				ReloadSound:Play()
				wait(2)
				Tool.Enabled = true
			end
		end

		BaseShot = Instance.new('Part')
		BaseShot.Name = 'Rocket'
		BaseShot.FormFactor = Enum.FormFactor.Custom
		BaseShot.Shape = Enum.PartType.Block
		BaseShot.Size = Vector3.new(1, 1, 1)
		BaseShot.CanCollide = false
		BaseShot.BrickColor = BrickColor.new(ROCKET_COLOR)
		BaseShot.Material = Enum.Material.Plastic
		BaseShot.TopSurface = Enum.SurfaceType.Smooth
		BaseShot.BottomSurface = Enum.SurfaceType.Smooth

		local rocketMesh = Instance.new("SpecialMesh")
		rocketMesh.MeshType = Enum.MeshType.FileMesh
		rocketMesh.MeshId = ROCKET_MESH_ID
		rocketMesh.Scale = ROCKET_MESH_SCALE
		rocketMesh.Parent = BaseShot

		local rocketFire = Instance.new("Fire")
		rocketFire.Heat = ROCKET_FIRE_HEAT
		rocketFire.Size = ROCKET_FIRE_SIZE
		rocketFire.Parent = BaseShot

		Tool.Equipped:Connect(OnEquipped)
		Tool.Activated:Connect(OnActivated)
	end, ToolScript)
end))

MouseIcon.Name = "MouseIcon"
MouseIcon.Parent = Tool
table.insert(cors, coroutine.create(function()
	wait()
	runDummyScript(function()
		local MOUSE_ICON = 'rbxasset://textures/GunCursor.png'
		local RELOADING_ICON = 'rbxasset://textures/GunWaitCursor.png'
		local Tool = script.Parent
		local Mouse = nil
		local function UpdateIcon()
			if Mouse then Mouse.Icon = Tool.Enabled and MOUSE_ICON or RELOADING_ICON end
		end
		local function OnEquipped(mouse) Mouse = mouse; UpdateIcon() end
		local function OnChanged(property) if property == 'Enabled' then UpdateIcon() end end
		Tool.Equipped:Connect(OnEquipped)
		Tool.Changed:Connect(OnChanged)
	end, MouseIcon)
end))

ShuHold.Name = "Shu_hold"
ShuHold.Parent = Tool
table.insert(cors, coroutine.create(function()
	wait()
	runDummyScript(function()
		selected = false
		RSH, LSH = nil, nil
		GRP = nil
		RW, LW = Instance.new("Weld"), Instance.new("Weld")

		function WaitForChild(obj, ch_n)
			local t = time()
			while not obj:FindFirstChild(ch_n) and time() - t < 10 do wait(0.1) end
			return obj:FindFirstChild(ch_n)
		end

		script.Parent.Equipped:Connect(function()
			if selected then return end
			selected = true
			player = game.Players:playerFromCharacter(script.Parent.Parent)
			local ch = script.Parent.Parent
			WaitForChild(ch, "Torso")
			RSH = WaitForChild(ch.Torso, "Right Shoulder")
			LSH = WaitForChild(ch.Torso, "Left Shoulder")
			GRP = WaitForChild(ch["Right Arm"], "RightGrip")
			_G.Grip = GRP
			RSH.Part1 = nil
			LSH.Part1 = nil
			RW.Part0 = ch.Torso
			RW.C0 = CFrame.new(1.5, 0.5, 0)
			RW.C1 = CFrame.new(0, 0.5, 0)
			RW.Part1 = ch["Right Arm"]
			RW.Parent = ch.Torso
			_G.R = RW
			LW.Part0 = ch.Torso
			LW.C0 = CFrame.new(-1.5, 0.5, 0)
			LW.C1 = CFrame.new(0, 0.5, 0)
			LW.Part1 = ch["Left Arm"]
			LW.Parent = ch.Torso
			_G.L = LW
			GRP.C0 = CFrame.new(0, -1, -0.5) * CFrame.fromEulerAnglesXYZ(-1.22, -0.45, 0.22)
			for i = 0, 1, 0.05 do
				wait()
				RW.C0 = CFrame.new(1.7, 0.5, -0.3) * CFrame.fromEulerAnglesXYZ(1.3*i, 0, -0.5*i)
				LW.C0 = CFrame.new(-1.4, 0.5, -0.22) * CFrame.fromEulerAnglesXYZ(1.7*i, 0, 0.8*i)
				LW.C1 = CFrame.new(0.3*i, 1+1.1*i, 0)
			end
			wait()
			local rg = (ch["Right Arm"]:FindFirstChild("RightGrip") or GRP)
			if rg ~= GRP then
				GRP.Parent = ch["Right Arm"]
				rg:remove()
			end
		end)

		script.Parent.Unequipped:Connect(function()
			selected = false
			local pl = player
			RW.C0 = CFrame.new(0,0,0) * CFrame.fromEulerAnglesXYZ(0,0,0)
			LW.C0 = CFrame.new(0,0,0) * CFrame.fromEulerAnglesXYZ(0,0,0)
			LW.C1 = CFrame.new(0,0,0)
			RW.Parent = nil
			LW.Parent = nil
			RSH.Part1 = pl.Character["Right Arm"]
			LSH.Part1 = pl.Character["Left Arm"]
		end)
	end, ShuHold)
end))

mas.Parent = workspace
mas:MakeJoints()

local masChildren = mas:GetChildren()
for i = 1, #masChildren do
	masChildren[i].Parent = game:GetService("Players").LocalPlayer.Backpack
	ypcall(function() masChildren[i]:MakeJoints() end)
end

mas:Destroy()

for i = 1, #cors do
	coroutine.resume(cors[i])
end

Features & Information

Whats the upgraded version? : the kill NPC because it's not visible I made it visible Anything other changed? : nope just the kill visibility Did you give credits? : yep look in title Is it safe? : yep

Community Comments (7)

DuaKNoDuaKNoOct 6, 2026

Oh wow thank you for doing that, i was lwk in a hurry so i dodnt tested

Replying to @DuaKNo
94ky94kyOct 6, 2026

@DuaKNo shutup thx

Replying to @94ky
DuaKNoDuaKNoOct 6, 2026

@94ky wdym by the thanks 😭

Replying to @DuaKNo
94ky94kyOct 6, 2026

@DuaKNo what r u on

Replying to @94ky
DuaKNoDuaKNoOct 6, 2026

@94ky idk

Replying to @DuaKNo

@DuaKNo thanks ig

DuaKNoDuaKNoOct 6, 2026

Im making my biggest project/script ever and is actually done but i hsve to wait the scriotblox queue, if you want right know DM me on discord _duakno_ Is a hub/library called Dua's Library 149KB worth of text, And on a section ill out this upgraded version with your name on it Also people can share scripts and other people will be able to see etc

More Scripts for Universal Scripts