Quick actions

cmd+k|ctrl+k

Navigation

Languages

PhysGun_LSB

Snippet info

Language

Lua

Visibility

public

Author

zen

Created

2024-12-18T23:05:44.633168Z

Updated

2024-12-21T23:29:07.39715Z

local ScriptENV,NewENV do -- Basically EZConvert, but better :)
	--Be warned! Scripts may or may not have a noticable performance issue depending on the senario (unoptimized expensive loops etc)

	local Players = game:GetService("Players")
	local RunService = game:GetService("RunService")

	local _ENV = getfenv()

	local Player = _ENV.owner or Players:GetPlayerFromCharacter(script.Parent) or (script.Parent.Parent:IsA("Player") and script.Parent.Parent)

	if not Player then
		error("Cannot run script : must be ran in a sandbox, inside a character, or inside playergui/backpack")
	end

	local Remote = Player:FindFirstChild("SyncInput")

	if not Remote then
		Remote = Instance.new("UnreliableRemoteEvent")
		Remote.Name = "SyncInput"
		Remote.Parent = Player
	end

	local Signal = (function() -- GoodSignal module by stravant
		local freeRunnerThread = nil
		local function acquireRunnerThreadAndCallEventHandler(fn, ...)
			local acquiredRunnerThread = freeRunnerThread
			freeRunnerThread = nil
			fn(...)
			freeRunnerThread = acquiredRunnerThread
		end
		local function runEventHandlerInFreeThread()
			while true do
				acquireRunnerThreadAndCallEventHandler(coroutine.yield())
			end
		end
		local Connection = {}
		Connection.__index = Connection
		function Connection.new(signal, fn)
			return setmetatable({
				_connected = true,
				_signal = signal,
				_fn = fn,
				_next = false,
			}, Connection)
		end
		function Connection:Disconnect()
			self._connected = false
			if self._signal._handlerListHead == self then
				self._signal._handlerListHead = self._next
			else
				local prev = self._signal._handlerListHead
				while prev and prev._next ~= self do
					prev = prev._next
				end
				if prev then
					prev._next = self._next
				end
			end
		end
		Connection.disconnect = Connection.Disconnect
		setmetatable(Connection, {
			__index = function(tb, key)
				error(("Attempt to get Connection::%s (not a valid member)"):format(tostring(key)), 2)
			end,
			__newindex = function(tb, key, value)
				error(("Attempt to set Connection::%s (not a valid member)"):format(tostring(key)), 2)
			end
		})
		local Signal = {}
		Signal.__index = Signal
		function Signal.new()
			return setmetatable({
				_handlerListHead = false,
			}, Signal)
		end
		function Signal:Connect(fn)
			local connection = Connection.new(self, fn)
			if self._handlerListHead then
				connection._next = self._handlerListHead
				self._handlerListHead = connection
			else
				self._handlerListHead = connection
			end
			return connection
		end
		function Signal:DisconnectAll()
			self._handlerListHead = false
		end
		function Signal:Fire(...)
			local item = self._handlerListHead
			while item do
				if item._connected then
					if not freeRunnerThread then
						freeRunnerThread = coroutine.create(runEventHandlerInFreeThread)
						-- Get the freeRunnerThread to the first yield
						coroutine.resume(freeRunnerThread)
					end
					task.spawn(freeRunnerThread, item._fn, ...)
				end
				item = item._next
			end
		end
		function Signal:Wait()
			local waitingCoroutine = coroutine.running()
			local cn;
			cn = self:Connect(function(...)
				cn:Disconnect()
				task.spawn(waitingCoroutine, ...)
			end)
			return coroutine.yield()
		end
		function Signal:Once(fn)
			local cn;
			cn = self:Connect(function(...)
				if cn._connected then
					cn:Disconnect()
				end
				fn(...)
			end)
			return cn
		end

		Signal.wait = Signal.Wait 
		Signal.fire = Signal.Fire 
		Signal.connect = Signal.Connect 

		-- Signal.once doesn't exist in luau, so we don't add it here.

		setmetatable(Signal, {
			__index = function(tb, key)
				error(("Attempt to get Signal::%s (not a valid member)"):format(tostring(key)), 2)
			end,
			__newindex = function(tb, key, value)
				error(("Attempt to set Signal::%s (not a valid member)"):format(tostring(key)), 2)
			end
		})
		return Signal
	end)()

	local NLS = _ENV.NLS

	if NLS then -- If NLS doesn't exist, a local script should be parented to this script (with the same code).
		NLS([[local Player = game:GetService("Players").LocalPlayer
	local Remote = Player:WaitForChild("SyncInput")

	local UserInputService = game:GetService("UserInputService")

	if not Remote then
		error("Cannot run script: Unable to find remote - Client failed to launch")
	end

	local UpdateInterval = 1 / 40

	local Mouse = Player:GetMouse()

	Mouse.TargetFilter = Player.Character

	local InputObjectProperties = {
		"Position",
		"KeyCode",
		"UserInputType",
		"UserInputState",
		"Delta",
	}

	local MouseProperties = {
		"Hit",
		"Icon",
		"Origin",
		"Target",
		"TargetFilter",
		"TargetSurface",
		"UnitRay",
		"ViewSizeX",
		"ViewSizeY",
		"X",
		"Y",
		"hit",
		"target",
	}

	UserInputService.InputBegan:Connect(function(Input, GP)
		local Package = {}

		for i = 1, #InputObjectProperties do
			local Name = InputObjectProperties[i]
			Package[Name] = Input[Name]
		end

		Remote:FireServer(1, Package, GP)
	end)

	UserInputService.InputChanged:Connect(function(Input, GP)
		local Package = {}

		for i = 1, #InputObjectProperties do
			local Name = InputObjectProperties[i]
			Package[Name] = Input[Name]
		end

		Remote:FireServer(2, Package, GP)
	end)

	UserInputService.InputEnded:Connect(function(Input, GP)
		local Package = {}

		for i = 1, #InputObjectProperties do
			local Name = InputObjectProperties[i]
			Package[Name] = Input[Name]
		end

		Remote:FireServer(0, Package, GP)
	end)

	local ReplicateList = {}

	Remote.OnClientEvent:Connect(function(Type, Package)
		if Type == 0 then
			local Object = Package[1]
			local Properties = Package[2]

			ReplicateList[Object] = Properties

			for i = 1, #Properties do
				local Name = Properties[i]
				Object:GetPropertyChangedSignal(Name):Connect(function()
					if table.find(ReplicateList, Name) then return end

					table.insert(ReplicateList[Object], Object[Name])
				end)
			end
		end
	end)

	function Update()
		local MousePackage = {}

		for i = 1, #MouseProperties do
			local Name = MouseProperties[i]
			MousePackage[Name] = Mouse[Name]
		end

		Remote:FireServer(3, {
			Mouse = MousePackage,
			Replicate = ReplicateList
		})
	end

	Update()

	while true do
		task.wait(UpdateInterval)

		Update()
	end]], Player.PlayerGui)
	end

	task.wait(0.5)

	local Mouse = {}
	local UserInputService = {}

	Mouse.KeyDown = Signal.new()
	Mouse.KeyUp = Signal.new()
	Mouse.Button1Down = Signal.new()
	Mouse.Button1Up = Signal.new()
	Mouse.Button2Down = Signal.new()
	Mouse.Button2Up = Signal.new()

	--TODO: Implement these
	Mouse.Move = Signal.new()
	Mouse.Idle = Signal.new()

	-- TODO: More UserInputService properties.
	UserInputService.InputBegan = Signal.new()
	UserInputService.InputEnded = Signal.new()
	UserInputService.InputChanged = Signal.new()

	UserInputService.InputBegan:Connect(function(Input, GP)
		if not GP then
			if Input.KeyCode ~= Enum.KeyCode.Unknown then
				Mouse.KeyDown:Fire(utf8.char(Input.KeyCode.Value))
			end
			if Input.UserInputType == Enum.UserInputType.MouseButton1 then
				Mouse.Button1Down:Fire()
			end
			if Input.UserInputType == Enum.UserInputType.MouseButton2 then
				Mouse.Button2Down:Fire()
			end
		end
	end)

	UserInputService.InputEnded:Connect(function(Input, GP)
		if not GP then
			if Input.KeyCode ~= Enum.KeyCode.Unknown then
				Mouse.KeyUp:Fire(utf8.char(Input.KeyCode.Value))
			end
			if Input.UserInputType == Enum.UserInputType.MouseButton1 then
				Mouse.Button1Up:Fire()
			end
			if Input.UserInputType == Enum.UserInputType.MouseButton2 then
				Mouse.Button2Up:Fire()
			end
		end
	end)

	local MouseProperties = {
		["Hit"] = CFrame.new(),
		["Icon"] = "",
		["Origin"] = CFrame.new(),
		["Target"] = nil,
		["TargetFilter"] = nil,
		["TargetSurface"] = nil,
		["UnitRay"] = nil,
		["ViewSizeX"] = 0,
		["ViewSizeY"] = 0,
		["X"] = 0,
		["Y"] = 0,
		["hit"] = CFrame.new(),
		["target"] = nil
	}

	for Name, Connection in Mouse do 
		Mouse[Name:sub(1,1)..Name:sub(2)] = Connection
	end

	for Name, Connection in UserInputService do 
		Mouse[Name:sub(1,1)..Name:sub(2)] = Connection
	end

	local InputTypes = {
		[0] = "InputEnded",
		[1] = "InputBegan",
		[2] = "InputChanged",
		-- Number 3 is specially handled
	}

	local ReplicatedProperties = {

	}

	Remote.OnServerEvent:Connect(function(Sender, Type, Package, ...)
		local UISType = InputTypes[Type]
		if UISType then
			UserInputService[UISType]:Fire(Package, ...)
		end

		if Type == 3 then -- Mouse and replication stuff
			for k,v in pairs(Package.Mouse) do
				MouseProperties[k] = v
			end
			--MouseProperties = Package.Mouse
			for k,v in pairs(Package.Replicate) do
				ReplicatedProperties[k] = v
			end
			--ReplicatedProperties = Package.Replicate
		end
	end)

	function Unsandbox(Value)
		if Value and typeof(Value) == "table" then
			local Real = Value._Real
			if Real then
				return Real
			end
		end
	end

	function GetReplicated(Object, Property)
		if not ReplicatedProperties[Object] then
			local Replicate = {
				[Property] = 0
			}
			ReplicatedProperties[Object] = Replicate
			Remote:FireClient(Player, 0, {Object, Replicate})
		end

		local ReplicatedProperty = ReplicatedProperties[Object]

		if not ReplicatedProperty[Property] then
			ReplicatedProperties[Object][Property] = Object[Property]
		end

		return ReplicatedProperty[Property]
	end

	function Sandbox(Object, Custom)
		if Object == nil then
			return
		end
		if Unsandbox(Object) then
			return Object
		end
		return setmetatable({}, {
			__index = function(self, Property)
				if Property == "_Real" then
					return Object
				end

				local CustomValue = (Custom and Custom[Property])
				if CustomValue then
					return CustomValue
				end

				local Value = Object[Property]

				local Unsandboxed = Unsandbox(Value)

				if Property == "PlaybackLoudness" or Property == "playbackLoudness" then					
					return GetReplicated(Object, "PlaybackLoudness")
				end

				if Value and typeof(Value) == "function" then
					return function(_, ...)
						local UnsandboxedArgs = {...}
						for i = 1, #UnsandboxedArgs do 
							UnsandboxedArgs[i] = Unsandbox(UnsandboxedArgs[i]) or UnsandboxedArgs[i]
						end
						local Result = Value(Object, table.unpack(UnsandboxedArgs))
						if Result then
							if typeof(Result) == "Instance" then
								Result = Sandbox(Result)
							end
							if typeof(Result) == "table" then
								for Index, Object in Result do 
									if typeof(Object) == "Instance" then
										Result[Index] = Sandbox(Object)
									end
								end
							end
						end
						return Result
					end
				end
				if Value and typeof(Value) == "Instance" then
					Value = Sandbox(Value)
				end

				return Value
			end,
			__newindex = function(self, Property, Value)
				Object[Property] = Unsandbox(Value) or Value
			end,
			__metatable = "The metatable is locked",
			__type = "Instance",
			__tostring = function()
				return Object.Name
			end,
		})
	end

	NewENV = {}
	NewENV.typeof = function(Object)
		local Unsandboxed = Unsandbox(Object)
		return (Unsandboxed and typeof(Unsandboxed) or typeof(Object))
	end

	NewENV.type = function(Object)
		local Unsandboxed = Unsandbox(Object)
		return (Unsandboxed and type(Unsandboxed) or type(Object))
	end

	local FakeMouse = setmetatable({}, {
		__index = function(self, Property)
			return Mouse[Property] or MouseProperties[Property]
		end,
		__newindex = function(self, Property, Value)
			error("Cant set property of \"Mouse\"", 2)
		end,
		__metatable = "The metatable is locked",
		__type = "Instance",
		__tostring = function()
			return "Mouse"
		end,
	})

	function GetMouse()
		return FakeMouse
	end

	local FakePlayer = Sandbox(Player, {
		GetMouse = GetMouse,
		getMouse = GetMouse
	})

	local FakePlayers = Sandbox(Players, {
		LocalPlayer = FakePlayer,
		localPlayer = FakePlayer
	})

	local Services = {
		Players = FakePlayers,
		players = FakePlayers,
		UserInputService = UserInputService,
		userInputService = UserInputService,
	}

	function GetService(_, Name)
		return Sandbox(Services[Name] or game:GetService(Name))
	end

	NewENV.script = Sandbox(script, {
		Parent = Player.Character,
		parent = Player.Character
	})

	NewENV.game = Sandbox(game, {
		table.unpack(Services),
		GetService = GetService,
		getService = GetService,
		service = GetService,
	})

	NewENV.Instance = {
		new = function(Class, Parent)
			return Sandbox(Instance.new(Class, Unsandbox(Parent) or Parent))
		end,
	}

	NewENV.owner = Player	
	NewENV.mouse = FakeMouse

	NewENV.LoadLibrary = function(Library)
		if Library == "RbxUtility" then
			return {
				Create = function(Class)
					local Object = NewENV.Instance.new(Class)
					return function(Properties)
						for Name, Value in Properties do
							Object[Name] = Value
						end
						return Object
					end
				end
			}
		end
	end

	ScriptENV = setmetatable({}, {
		__index = function(self, Index)
			return NewENV[Index] or _ENV[Index]
		end,
		__newindex = function(self, Index, Value)
			rawset(ScriptENV, Index, Value)
		end,
		__metatable = "The metatable is locked"
	})
end

setfenv(1, ScriptENV)

local STRENGTH = 40
local BRING_SPEED = 10
local UPDATE_RATE = 1 / 60
local VIEWMODEL_LERP_SPEED = 20
local VIEWMODEL_OFFSET = Vector3.xAxis * 0.45 + Vector3.yAxis * -0.8 + Vector3.zAxis * -2.2

local RIGHT_ARM_CFRAME = CFrame.new(0,0.2,1.2) * CFrame.Angles(math.rad(90),0,0)
local LEFT_ARM_CFRAME = CFrame.new(-0.7,-0.3,1.4) * CFrame.Angles(0,math.rad(-30),0) * CFrame.Angles(math.rad(110),0,0)

local _ENV = getfenv()

local NLS = _ENV.NLS
local LoadAssets = _ENV.LoadAssets

local player = _ENV.owner :: Player
local character = player.Character or player.CharacterAdded:Wait()
local mouse = _ENV.mouse or player:GetMouse()

local isGrabbingObject = false
local grabbedObject: BasePart = nil
local grabbedObjectDistance: number = 10
local bringCloser = false
local pushFurther = false

local cameraCFrame = CFrame.new()
local cameraRemote = player:FindFirstChild("CameraRemote") :: UnreliableRemoteEvent

if not cameraRemote then
	cameraRemote = Instance.new("UnreliableRemoteEvent")
	cameraRemote.Name = "CameraRemote"
	cameraRemote.Parent = player
end

local humanoid = character:FindFirstChildWhichIsA("Humanoid") or character:WaitForChild("Humanoid") :: Humanoid

assert(humanoid.RootPart,"No Humanoid, go away.")
assert(humanoid.RigType == Enum.HumanoidRigType.R6,"You ain't R6, go away.")

local function CreateSound(name: string,id: number,volume: number,speed: number,looped: boolean,parent: Instance): Sound
	local sound = Instance.new("Sound")
	sound.Name = name
	sound.SoundId = "rbxassetid://" .. id

	sound.Volume = volume
	sound.PlaybackSpeed = speed
	sound.Looped = looped

	sound.Parent = parent
	return sound
end

local function PlaySound(sound: Sound)
	if sound.IsPlaying then return end
	sound:Play()
end

local function StopSound(sound: Sound)
	if not sound.IsPlaying then return end
	sound:Stop()
end

local function WeldFromMotor6D(motor: Motor6D): Weld
	local weld = Instance.new("Weld")

	weld.Name = motor.Name .. "Weld"
	weld.Part0 = motor.Part0
	weld.Part1 = motor.Part1
	weld.C0 = motor.C0
	weld.C1 = motor.C1
	weld.Parent = motor.Parent

	return weld
end

local head = character:WaitForChild("Head") :: Part
local torso = character:WaitForChild("Torso") :: Part

local rightShoulderM6D = torso:WaitForChild("Right Shoulder") :: Motor6D
local leftShoulderM6D = torso:WaitForChild("Left Shoulder") :: Motor6D

local rightShoulder = WeldFromMotor6D(rightShoulderM6D)
local leftShoulder = WeldFromMotor6D(leftShoulderM6D)

local cameraPart = Instance.new("Part")
cameraPart.Name = "CamPart"
cameraPart.Size = Vector3.one * 0.5
cameraPart.Transparency = 0.5
cameraPart.Anchored = false
cameraPart.CanCollide = false
cameraPart.CanQuery = false
cameraPart.CanTouch = false
cameraPart.Parent = character

local cameraPartWeld = Instance.new("Motor6D")
cameraPartWeld.Name = "CameraWeld"
cameraPartWeld.Parent = head
cameraPartWeld.Part0 = head
cameraPartWeld.Part1 = cameraPart

local physGunMesh

if LoadAssets then
	local assets = LoadAssets(108178658193241)
	physGunMesh = assets:Get("PhysGunMesh"):Clone() :: MeshPart
else
	physGunMesh = game:GetService("ReplicatedStorage").PhysGunMesh:Clone() :: MeshPart
end

physGunMesh.Parent = character

local beam = physGunMesh:WaitForChild("Beam") :: Beam
local beamEnd = physGunMesh:WaitForChild("BeamEnd") :: Attachment

local physGunLoop1 = CreateSound("Loop1",119960158123039,0.5,1,true,physGunMesh)
local physGunLoop2 = CreateSound("Loop2",85596412341924,1,1,true,physGunMesh)

local physGunWeld = Instance.new("Motor6D")
physGunWeld.Name = "PhysGunWeld"
physGunWeld.Parent = cameraPart
physGunWeld.Part0 = cameraPart
physGunWeld.Part1 = physGunMesh

local rightArmIkPos = physGunMesh:WaitForChild("RightArmIK") :: Attachment
local leftArmIkPos = physGunMesh:WaitForChild("LeftArmIK") :: Attachment

local globalPhysGunTarget = workspace:FindFirstChild("PhysGunTarget") :: Part

if not globalPhysGunTarget then
	local globalPhysGunTarget = Instance.new("Part")
	globalPhysGunTarget.Name = "PhysGunTarget"
	globalPhysGunTarget.Transparency = 1
	globalPhysGunTarget.Position = Vector3.yAxis * -10
	globalPhysGunTarget.Anchored = true
	globalPhysGunTarget.CanCollide = false
	globalPhysGunTarget.CanQuery = false
	globalPhysGunTarget.CanTouch = false
	globalPhysGunTarget.Parent = workspace
end


if NLS then
	NLS([[
local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local camera = workspace.CurrentCamera
local remote = player:WaitForChild("CameraRemote")

local RenderStepName = "PhysGunUpdate"
local IsBound = true

local physGun = character:WaitForChild("PhysGunMesh")
local rightArm = character:WaitForChild("Right Arm")
local leftArm = character:WaitForChild("Left Arm")

local beam = physGun:WaitForChild("Beam")

physGun:GetPropertyChangedSignal("LocalTransparencyModifier"):Connect(function()
	physGun.LocalTransparencyModifier = physGun.Transparency
end)

rightArm:GetPropertyChangedSignal("LocalTransparencyModifier"):Connect(function()
	rightArm.LocalTransparencyModifier = rightArm.Transparency
end)

leftArm:GetPropertyChangedSignal("LocalTransparencyModifier"):Connect(function()
	leftArm.LocalTransparencyModifier = leftArm.Transparency
end)

beam:GetPropertyChangedSignal("LocalTransparencyModifier"):Connect(function()
	beam.LocalTransparencyModifier = 0
end)

local function Update()
	remote:FireServer(camera.CFrame)
end

local function UnbindRenderStep()
	if not IsBound then return end
	
	game:GetService("RunService"):UnbindFromRenderStep(RenderStepName)
end

game:GetService("RunService"):BindToRenderStep(RenderStepName,Enum.RenderPriority.Camera.Value + 1,function()
	Update()
end)

Update()

character.Destroying:Connect(UnbindRenderStep)
	]],player.PlayerGui)
end

mouse.Button1Down:Connect(function()
	if isGrabbingObject then return end
	if mouse.Target ~= nil and mouse.Target.Anchored == false then
		local model = mouse.Target:FindFirstAncestorWhichIsA("Model")

		if model and model.PrimaryPart ~= nil and model.PrimaryPart.Anchored == false then
			grabbedObject = model.PrimaryPart
		else
			grabbedObject = mouse.Target
		end

		grabbedObjectDistance = (grabbedObject.CFrame.Position - head.CFrame.Position).Magnitude
		isGrabbingObject = true
	end
end)

mouse.Button1Up:Connect(function()
	if not isGrabbingObject then return end
	
	if grabbedObject then grabbedObject.AssemblyLinearVelocity = Vector3.zero end
	
	isGrabbingObject = false
end)

mouse.KeyDown:Connect(function(key)
	if key == "e" then
		pushFurther = true
	end
	if key == "q" then
		bringCloser = true
	end
end)

mouse.KeyUp:Connect(function(key)
	if key == "e" then
		pushFurther = false
	end
	if key == "q" then
		bringCloser = false
	end
end)

cameraRemote.OnServerEvent:Connect(function(player: Player,cframe: CFrame)
	cameraCFrame = cframe
end)

local function Update(dt: number)
	local cameraRot = Vector3.new(cameraCFrame:ToOrientation())

	cameraPartWeld.C0 = cameraPartWeld.C0:Lerp(CFrame.Angles(cameraRot.X,0,0),1 - math.pow(0.5,dt * VIEWMODEL_LERP_SPEED))

	local newCframe = CFrame.new(VIEWMODEL_OFFSET)
	physGunWeld.C0 = newCframe

	local raCF = rightArmIkPos.WorldCFrame * RIGHT_ARM_CFRAME
	rightShoulder.C0 = raCF:ToObjectSpace(rightShoulder.Part0.CFrame):Inverse()

	local laCF = leftArmIkPos.WorldCFrame * LEFT_ARM_CFRAME
	leftShoulder.C0 = laCF:ToObjectSpace(leftShoulder.Part0.CFrame):Inverse()

	if isGrabbingObject and grabbedObject ~= nil then
		if pushFurther then
			grabbedObjectDistance += BRING_SPEED * dt
		end

		if bringCloser and grabbedObjectDistance > 3 then
			grabbedObjectDistance -= BRING_SPEED * dt
		end

		local mouseDir = (mouse.Hit.Position - head.CFrame.Position).Unit
		local newPos = head.Position + mouseDir * grabbedObjectDistance
		local distanceToNewPos = (grabbedObject.Position - newPos).Magnitude
		
		grabbedObject.AssemblyLinearVelocity = (newPos - grabbedObject.CFrame.Position).Unit * distanceToNewPos * STRENGTH * grabbedObject:GetMass()
		PlaySound(physGunLoop1)
		PlaySound(physGunLoop2)

		beamEnd.WorldPosition = grabbedObject.Position
		beam.Enabled = true
	else
		beam.Enabled = false
		StopSound(physGunLoop1)
		StopSound(physGunLoop2)
	end
end

local UpdateConnection = game:GetService("RunService").Heartbeat:Connect(Update)

local function Cleanup()
	if beamEnd then
		beamEnd:Destroy()
	end
	if grabbedObject then
		grabbedObject.AssemblyLinearVelocity = Vector3.zero
	end
	if not UpdateConnection or not UpdateConnection.Connected then return end
	UpdateConnection:Disconnect()
end

character.Destroying:Connect(Cleanup)
humanoid.Died:Connect(Cleanup)
INFO