Pinkstack/roblox-luau-corpus-text huggingface dataset:
-- This is basically a function that finds all unanchored parts and adds them to childList.
-- Note: This should only be run once for each object


function checkObject(obj)
	if (obj ~= hole) and (obj.className == "Part" and obj.Parent~=hole.Parent and obj~=tele2) then
		if (obj.Anchored == false) then
			table.insert(childList, 1, obj)
		end
	elseif (obj.className == "Model") or (obj.className == "Hat") or (obj.className == "Tool") or (obj == workspace) then
		local child = obj:GetChildren()
		for x = 1, #child do
			checkObject(child[x])
		end
		obj.ChildAdded:connect(checkObject)
	end
end

checkObject(workspace)

print("Black Hole script loaded.")

local n = 0
while true do
	if n < #childList then
		n = n + 1
		if n % 800 == 0 then
			wait()
		end
	else
		n = 1
		wait()
	end

	local child = childList[n]
	if (child ~= hole) and (child.className == "Part") and (child.Anchored == false) then
		local relPos = hole.Position - child.Position
		local motivator = child:FindFirstChild("BlackHole Influence")
		if relPos.magnitude * 240 * massConstant < mass then
			if (relPos.magnitude * 320 * massConstant < mass) and (child.Size.z + hole.Size.x >  relPos.magnitude * 2 - 4) then
				mass = mass + child:GetMass()
				table.remove(childList, n)
				n = n - 1 -- This is the reason I need a counter of my own design
			else
				child.CanCollide = false -- I Can assume that things won't escape the black hole.
				if motivator == nil then
					motivator = Instance.new("BodyPosition")
					motivator.Parent = child
					motivator.Name = "BlackHole Influence"
				end
				motivator.position = hole.Position
				motivator.maxForce = Vector3.new(1, 1, 1) * mass * child:GetMass() / (relPos.magnitude * massConstant)
			end
		elseif motivator ~= nil then
			motivator:Remove()
		end
	end
end
--rayPart					= Instance.new("Part")
--rayPart.Name			= "MG Ray"
--rayPart.Transparency	= .5
--rayPart.Anchored		= true
--rayPart.CanCollide		= false
--rayPart.TopSurface		= Enum.SurfaceType.Smooth
--rayPart.BottomSurface	= Enum.SurfaceType.Smooth
--rayPart.formFactor		= Enum.FormFactor.Custom
--rayPart.BrickColor		= BrickColor.new("Bright yellow")
--rayPart.Reflectance		= 0
--rayPart.Material = Enum.Material.SmoothPlastic



function fireCoax()
	local MGAmmo = tankStats.MGAmmo;
	if MGAmmo.Value <= 0 then
		return false;
	end;
	
	TFE:FireServer('FireMinigun',parts,tankStats,MGDamage,script.Parent)
	
	wait(1/350*60);
	
	return true;
end
-- Remeber to select the animtion object and set the id to your own!


local walkAnim = script:WaitForChild("Walk")
local walkAnimTrack = humanoid.Animator:LoadAnimation(walkAnim)

humanoid.Running:Connect(function(speed)
	if speed > 0 then
		if not walkAnimTrack.IsPlaying then
			walkAnimTrack:Play()
		end
	else
		if walkAnimTrack.IsPlaying then
			walkAnimTrack:Stop()
		end
	end
end)
--[[

	CastVisualiser Visuals.new(Color: Color3?, WorldRoot: WorldRoot?)
		Creates and returns a CastVisualiser class.

--]]



local Meta = {__index = CastVisualiser}

function Visuals.new(Color: Color3?, WorldRoot: WorldRoot?): CastVisualiser
	local self: CastVisualiserPrivate = setmetatable({
		Color = Color or Color3.new(0, 0, 1),
		WorldRoot = WorldRoot or workspace,
	}, Meta)
	
	-- Origin Part
	local CastOriginPart = Instance.new("Part")
	CastOriginPart.Transparency = 1
	CastOriginPart.CanCollide = false
	CastOriginPart.CanQuery = false
	CastOriginPart.CanTouch = false
	CastOriginPart.Anchored = true
	CastOriginPart.Size = Vector3.one / 100

	self.CastOriginPart = CastOriginPart
	
	-- Visuals
	local LineHandleAdornment = Instance.new("LineHandleAdornment")
	LineHandleAdornment.Color3 = self.Color
	LineHandleAdornment.AlwaysOnTop = ALWAYS_ON_TOP
	LineHandleAdornment.Transparency = TRANSPARENCY
	LineHandleAdornment.Thickness = LINE_THICKNESS
	LineHandleAdornment.ZIndex = 1
	LineHandleAdornment.Adornee = CastOriginPart
	LineHandleAdornment.Parent = CastOriginPart
	
	self.LineVisual = LineHandleAdornment
	
	local ConeHandleAdornmnet = Instance.new("ConeHandleAdornment")
	ConeHandleAdornmnet.Color3 = self.Color
	ConeHandleAdornmnet.AlwaysOnTop = ALWAYS_ON_TOP
	ConeHandleAdornmnet.Transparency = TRANSPARENCY
	ConeHandleAdornmnet.Radius = 0.05
	ConeHandleAdornmnet.Height = CONE_HEIGHT
	ConeHandleAdornmnet.SizeRelativeOffset = Vector3.new(0, 0, CONE_HEIGHT * 2)
	ConeHandleAdornmnet.ZIndex = 1
	ConeHandleAdornmnet.Adornee = CastOriginPart
	ConeHandleAdornmnet.Parent = CastOriginPart
	
	self.ConeVisual = ConeHandleAdornmnet
	
	local BoxHandleAdornment = Instance.new("BoxHandleAdornment")
	BoxHandleAdornment.Color3 = self.Color
	BoxHandleAdornment.AlwaysOnTop = ALWAYS_ON_TOP
	BoxHandleAdornment.Transparency = TRANSPARENCY
	BoxHandleAdornment.ZIndex = 0
	BoxHandleAdornment.Adornee = CastOriginPart
	BoxHandleAdornment.Parent = CastOriginPart
	
	self.BoxVisual = BoxHandleAdornment
	
	local SphereHandleAdornment = Instance.new("SphereHandleAdornment")
	SphereHandleAdornment.Color3 = self.Color
	SphereHandleAdornment.AlwaysOnTop = ALWAYS_ON_TOP
	SphereHandleAdornment.Transparency = TRANSPARENCY
	SphereHandleAdornment.ZIndex = 0
	SphereHandleAdornment.Adornee = CastOriginPart
	SphereHandleAdornment.Parent = CastOriginPart
	
	self.SphereVisual = SphereHandleAdornment
	
	return self
end

return Visuals
--[=[
	Accepts an array of Promises and returns a Promise that is resolved as soon as `count` Promises are resolved from the input array. The resolved array values are in the order that the Promises resolved in. When this Promise resolves, all other pending Promises are cancelled if they have no other consumers.

	`count` 0 results in an empty array. The resultant array will never have more than `count` elements.

	```lua
	local promises = {
		returnsAPromise("example 1"),
		returnsAPromise("example 2"),
		returnsAPromise("example 3"),
	}

	return Promise.some(promises, 2) -- Only resolves with first 2 promises to resolve
	```

	@param promises {Promise<T>}
	@param count number
	@return Promise<{T}>
]=]


function Promise.some(promises, count)
	assert(type(count) == "number", "Bad argument #2 to Promise.some: must be a number")

	return Promise._all(debug.traceback(nil, 2), promises, count)
end
-- Determine the gun elevation and tilt


function determineNewDirections(targetVector)
	local targElev = 0;
	local targRot = 0;
	
	local turretMain = 	parts.TurretRing2;
	
	-- Vector normal to the plane which is the turret ring
	local turretNormal = turretMain.CFrame.lookVector;
	
	local angleFromNormal = turretNormal:Dot(targetVector);
	
	-- Determine gun elevation
	targElev = math.pi/2 - math.acos(angleFromNormal);
	targRot= math.pi/2 - math.acos(angleFromNormal);	

	-- Projection of target vector onto the turret plane
	local targetProjection = (targetVector - (angleFromNormal*turretNormal)).unit;	

	local forwardVector = ( (turretMain.CFrame - turretMain.CFrame.p) -- Orientation only of brick
		* CFrame.new(0, -1, 0)).p; -- Translated down 1
	
	-- Determine angle between forward vector and the projected target vector
	targRot = math.acos(forwardVector:Dot(targetProjection));
	
	-- Determine the sign of the angle
	local vectorCross = forwardVector:Cross(targetProjection);
	
	-- If the cross product is opposite to the normal vector, make the angle negative
	if (vectorCross:Dot(turretNormal) < 0) then
		targRot = -targRot;
	end
	
	-- Put rotation and elevation into an array
	local rotations = {};
	rotations[1] = targElev;
	rotations[2] = targRot;
	
	return rotations;
end

function sign(input)
	if (input < 0) then
		return -1;
	else
		return 1;
	end
end
--Required components


local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local table = require(script.table) --If you're using this from GitHub, this is the table in the boilerplate folder.
local RegularSave = false
local RegularSaveNum = 300
local SaveInStudioObject = game:GetService("ServerStorage"):FindFirstChild("SaveInStudio")
local SaveInStudio = SaveInStudioObject and SaveInStudioObject.Value
local Debug = false

local Verifier = {}

function Verifier.typeValid(data)
	return type(data) ~= "userdata", typeof(data)
end

function Verifier.scanValidity(tbl, passed, path)
	if type(tbl) ~= "table" then
		return Verifier.scanValidity({input = tbl}, {}, {})
	end
	passed, path = passed or {}, path or {"input"}
	passed[tbl] = true
	local tblType
	do
		local key, value = next(tbl)
		if type(key) == "number" then
			tblType = "Array"
		else
			tblType = "Dictionary"
		end
	end
	local last = 0
	for key, value in next, tbl do
		path[#path + 1] = tostring(key)
		if type(key) == "number" then
			if tblType == "Dictionary" then
				return false, path, "Mixed Array/Dictionary"
			elseif key%1 ~= 0 then  -- if not an integer
				return false, path, "Non-integer index"
			elseif key == math.huge or key == -math.huge then
				return false, path, "(-)Infinity index"
			end
		elseif type(key) ~= "string" then
			return false, path, "Non-string key", typeof(key)
		elseif tblType == "Array" then
			return false, path, "Mixed Array/Dictionary"
		end
		if tblType == "Array" then
			if last ~= key - 1 then
				return false, path, "Array with non-sequential indexes"
			end
			last = key
		end
		local isTypeValid, valueType = Verifier.typeValid(value)
		if not isTypeValid then
			return false, path, "Invalid type", valueType
		end
		if type(value) == "table" then
			if passed[value] then
				return false, path, "Cyclic"
			end
			local isValid, keyPath, reason, extra = Verifier.scanValidity(value, passed, path)
			if not isValid then
				return isValid, keyPath, reason, extra
			end
		end
		path[#path] = nil
	end
	passed[tbl] = nil
	return true
end

function Verifier.getStringPath(path)
	return table.concat(path, ".")
end

function Verifier.warnIfInvalid(input)
	local isValid, keyPath, reason, extra = Verifier.scanValidity(input)
	if not isValid then
		if extra then
			warn("Invalid at "..Verifier.getStringPath(keyPath).." because: "..reason.." ("..tostring(extra)..")")
		else
			warn("Invalid at "..Verifier.getStringPath(keyPath).." because: "..reason)
		end
	end

	return isValid
end

local function clone(value)
	if typeof(value) == "table" then
		return table.deep(value)
	else
		return value
	end
end
--[[Brakes]]


	Tune.ABSEnabled		= true		-- Implements ABS
	Tune.ABSThreshold	= 20		-- Slip speed allowed before ABS starts working (in SPS) 

	Tune.FBrakeForce	= 3000		-- Front brake force
	Tune.RBrakeForce	= 2500		-- Rear brake force
	Tune.PBrakeForce	= 13000		-- Handbrake force
	
	Tune.FLgcyBForce	= 41000		-- Front brake force [PGS OFF]
	Tune.RLgcyBForce	= 36000		-- Rear brake force [PGS OFF]
	Tune.LgcyPBForce	= 150000    -- Handbrake force [PGS OFF]
-------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------


	
	
function onRunning(speed)
	if speed>0 then
		playAnimation("walk", 0.1, Humanoid)
		pose = "Running"
	else
		playAnimation("idle", 0.1, Humanoid)
		pose = "Standing"
	end
end
	
function onDied()
	pose = "Dead"
end
	
function onJumping()
	playAnimation("jump", 0.1, Humanoid)
	jumpAnimTime = jumpAnimDuration
	pose = "Jumping"
end
	
function onClimbing()
	playAnimation("climb", 0.1, Humanoid)
	pose = "Climbing"
end
	
function onGettingUp()
	pose = "GettingUp"
end
	
function onFreeFall()
	if (jumpAnimTime <= 0) then
		playAnimation("fall", fallTransitionTime, Humanoid)
	end
	pose = "FreeFall"
end
	
function onFallingDown()
	pose = "FallingDown"
end
	
function onSeated()
	pose = "Seated"
end
	
function onPlatformStanding()
	pose = "PlatformStanding"
end
	
function onSwimming(speed)
	if speed>0 then
		pose = "Running"
	else
		pose = "Standing"
	end
end
	
function getTool()	
	for _, kid in ipairs(Figure:GetChildren()) do
		if kid.className == "Tool" then return kid end
	end
	return nil
end
	
function getToolAnim(tool)
	for _, c in ipairs(tool:GetChildren()) do
		if c.Name == "toolanim" and c.className == "StringValue" then
			return c
		end
	end
	return nil
end
	
function animateTool()
		
	if (toolAnim == "None") then
		playToolAnimation("toolnone", toolTransitionTime, Humanoid)
		return
	end
		
	if (toolAnim == "Slash") then
		playToolAnimation("toolslash", 0, Humanoid)
		return
	end
		
	if (toolAnim == "Lunge") then
		playToolAnimation("toollunge", 0, Humanoid)
		return
	end
end
	
function moveSit()
	RightHip:SetDesiredAngle(3.14 /2)
	LeftHip:SetDesiredAngle(-3.14 /2)
end
	
local lastTick = 0
	
function move(time)
	local amplitude = 1
	local frequency = 1
	local deltaTime = time - lastTick
	lastTick = time
		
	local climbFudge = 0
	local setAngles = false
		
	if (jumpAnimTime > 0) then
		jumpAnimTime = jumpAnimTime - deltaTime
	end
		
	if (pose == "FreeFall" and jumpAnimTime <= 0) then
		playAnimation("fall", fallTransitionTime, Humanoid)
	elseif (pose == "Seated") then
		stopAllAnimations()
		moveSit()
		return
	elseif (pose == "Running") then
		playAnimation("walk", 0.1, Humanoid)
	elseif (pose == "Dead" or pose == "GettingUp" or pose == "FallingDown" or pose == "Seated" or pose == "PlatformStanding") then
		--		print("Wha " .. pose)
		amplitude = 0.1
		frequency = 1
		setAngles = true
	end
		
	if (setAngles) then
		desiredAngle = amplitude * math.sin(time * frequency)
			
		RightHip:SetDesiredAngle(-desiredAngle)
		LeftHip:SetDesiredAngle(-desiredAngle)
	end
		
	-- Tool Animation handling
	local tool = getTool()
	if tool then
			
		animStringValueObject = getToolAnim(tool)
			
		if animStringValueObject then
			toolAnim = animStringValueObject.Value
			-- message recieved, delete StringValue
			animStringValueObject.Parent = nil
			toolAnimTime = time + .3
		end
			
		if time > toolAnimTime then
			toolAnimTime = 0
			toolAnim = "None"
		end
			
		animateTool()		
	else
		stopToolAnimations()
		toolAnim = "None"
		toolAnimTime = 0
	end
end
--[[
	    ___      _______                _       _______
	   / _ |____/ ___/ /  ___ ____ ___ (_)__   /__  __/
	  / __ /___/ /__/ _ \/ _ `(_-<(_-</ (_-<     / /
	 /_/ |_|   \___/_//_/\_,_/___/___/_/___/	/_/   
						SecondLogic @ Inspare
						Avxnturador @ Novena
]]




local autoscaling	= true					--Estimates top speed

local UNITS	= {								--Click on speed to change units
												--First unit is default	
	{
		units			= "MPH"				,
		scaling			= (10/12) * (60/88)	, -- 1 stud : 10 inches | ft/s to MPH
		maxSpeed		= 230				,
		spInc			= 20				, -- Increment between labelled notches
	},
}
--p:WaitForChild("detect").Weld.C0 = CFrame.new(Vector3.new(-4.09,0,0))
--p:WaitForChild("detect").Weld.C1 = CFrame.new(Vector3.new(0,0,0))
--i hate roblox welds


local last = os.time()
p.spotter.Touched:Connect(function(hit: Part)
	while #game.Workspace.CurLevel:GetChildren() == 0  do
		task.wait()
	end
	if hit.Parent:FindFirstChild("Humanoid") and os.time() - last >= 0.1 and active and not hit:IsDescendantOf(game.Workspace.CurLevel:GetChildren()[1].PuzzleElements.Guards) then
		last = os.time()
		if hit.Parent:FindFirstChild("Humanoid").Health > 0 then
			hit.Parent:FindFirstChild("Humanoid").Health -= 49
		end
	end
end)

explodeBindable.Event:Connect(function()
	if active then
		active = false
		p.camera.Indicator.Color = Color3.fromRGB(255,0,0)
		p.camera.top.Anchored = false
		p.spotter.Transparency = 1
		camera1st:Cancel()
		camera2nd:Cancel()
		spotter1st:Cancel()
		spotter2nd:Cancel()
	end
end)

while task.wait() and active do
	camera1st:Play()
	spotter1st:Play()
	spotter1st.Completed:Wait()
	task.wait(config.GoalWaitTime.Value)
	camera2nd:Play()
	spotter2nd:Play()
	spotter2nd.Completed:Wait()
	task.wait(config.GoalWaitTime.Value)
end
-------- OMG HAX



r = game:service("RunService")


Tool = script.Parent

local equalizingForce = 236 / 1.2 -- amount of force required to levitate a mass
local gravity = .9999999999999999 -- things float at > 1
local jumpScript = require(4965769761)

local ghostEffect = nil
local massCon1 = nil
local massCon2 = nil

function recursiveGetLift(node)
	local m = 0
	local c = node:GetChildren()
	for i=1,#c do
		if c[i].className == "Part" then
			if c[i].Name == "Handle" then
				m = m + (c[i]:GetMass() * equalizingForce * 1) -- hack that makes hats weightless, so different hats don't change your jump height
			else
				m = m + (c[i]:GetMass() * equalizingForce * gravity)
			end
		end
		m = m + recursiveGetLift(c[i])
	end
	return m
end


function onMassChanged(child, char)
	print("Mass changed:" .. child.Name .. "    " .. char.Name)
	if (ghostEffect ~= nil) then
		ghostEffect.force = Vector3.new(0, recursiveGetLift(char) ,0)
	end
end



function UpdateGhostState(isUnequipping)

	if isUnequipping == true then
		ghostEffect:Remove()
		ghostEffect = nil
		massCon1:disconnect()
		massCon2:disconnect()
	else
		if ghostEffect == nil then
			local char = Tool.Parent
			if char == nil then return end
			ghostEffect = Instance.new("BodyForce")
			ghostEffect.Name = "GravityCoilEffect"
			ghostEffect.force = Vector3.new(0, recursiveGetLift(char) ,0)
			ghostEffect.Parent = char.Head
			ghostChar = char
			massCon1 = char.ChildAdded:connect(function(child)  onMassChanged(child, char) end)
			massCon2 = char.ChildRemoved:connect(function(child)  onMassChanged(child, char) end)
		end
	end
	

end



function onEquipped()
	Tool.Handle.CoilSound:Play()
	UpdateGhostState(false)
end

function onUnequipped()
	UpdateGhostState(true)
end


script.Parent.Equipped:connect(onEquipped)
script.Parent.Unequipped:connect(onUnequipped)
--[=[
	Observes the current value of the ValueObject
	@return Observable<T>
]=]


function ValueObject:Observe()
	return Observable.new(function(sub)
		if not self.Destroy then
			warn("[ValueObject.observeValue] - Connecting to dead ValueObject")
			-- No firing, we're dead
			sub:Complete()
			return
		end

		local maid = Maid.new()

		maid:GiveTask(self.Changed:Connect(function()
			sub:Fire(self.Value)
		end))

		sub:Fire(self.Value)

		return maid
	end)

end
--]]




function getHumanoid(model)
	for _, v in pairs(model:GetChildren()) do
		if v:IsA'Humanoid' then
			return v
		end
	end
end


local zombie = script.Parent
local human = getHumanoid(zombie)
local hroot = zombie.HumanoidRootPart
local zspeed = hroot.Velocity.magnitude


local pfs = game:GetService("PathfindingService")

function GetPlayerNames()
	local players = game:GetService('Players'):GetChildren()
	local name = nil
	for _, v in pairs(players) do
		if v:IsA'Player' then
			name = tostring(v.Name)
		end
	end
	return name
end

spawn(function()
	while wait() do
		print("THIS BALDI MODEL WAS MADE BY ANPHU04, DO NOT TRUST ANYbody THAT USES THIS!")
	end
end)

function GetPlayersbodyParts(t)
	local body = t
	if body then
		local figure = body.Parent
		for _, v in pairs(figure:GetChildren()) do
			if v:IsA'Part' then
				return v.Name
			end
		end
	else
		return "HumanoidRootPart"
	end
end

function Getbody(part)
	local chars = game.Workspace:GetChildren()
	local body = nil
	for _, v in pairs(chars) do
		if v:IsA'Model' and v ~= script.Parent and v.Name == GetPlayerNames() then
			local charRoot = v:FindFirstChild'HumanoidRootPart'
			if (charRoot.Position - part).magnitude < SearchDistance then
				body = charRoot
			end
		end
	end
	return body
end

for _, zambieparts in pairs(zombie:GetChildren()) do
	if zambieparts:IsA'Part' then
		zambieparts.Touched:connect(function(p)
			if p.Parent.Name == GetPlayerNames() and p.Parent.Name ~= zombie.Name then -- damage
				local enemy = p.Parent
				local enemyhuman = getHumanoid(enemy)
				enemyhuman:TakeDamage(ZombieDamage)
			end
		end)
	end
end
--[[Drivetrain]]

	
	Tune.Config			= "RWD"		--"FWD" , "RWD" , "AWD"
	Tune.TorqueVector	= 0			--AWD ONLY, "-1" has a 100% front bias, "0" has a 50:50 bias, and "1" has a 100% rear bias. Can be any number between that range.
	
	--Differential Settings	
	Tune.FDiffSlipThres	= 50		-- 1 - 100%				(Max threshold of applying full lock determined by deviation from avg speed)
	Tune.FDiffLockThres	= 50		-- 0 - 100%				(0 - Bias toward slower wheel, 100 - Bias toward faster wheel)
	Tune.RDiffSlipThres	= 80		-- 1 - 100%
	Tune.RDiffLockThres	= 20		-- 0 - 100%
	Tune.CDiffSlipThres	= 50		-- 1 - 100%				[AWD Only]
	Tune.CDiffLockThres	= 50		-- 0 - 100%				[AWD Only]
	
	--Traction Control Settings
	Tune.TCSEnabled		= true		-- Implements TCS
	Tune.TCSThreshold	= 20		-- Slip speed allowed before TCS starts working (in SPS)
	Tune.TCSGradient	= 20		-- Slip speed gradient between 0 to max reduction (in SPS)
	Tune.TCSLimit		= 10		-- Minimum amount of torque at max reduction (in percent)
--[[Weight and CG]]


Tune.Weight			= 1700		-- Total weight (in pounds)
Tune.WeightBSize	= {			-- Size of weight brick (dimmensions in studs ; larger = more stable)
	--[[Width]]		6		,
	--[[Height]]		3.5		,
	--[[Length]]		14		}
Tune.WeightDist		= 55		-- Weight distribution (0 - on rear wheels, 100 - on front wheels, can be <0 or >100)
Tune.CGHeight		= .8		-- Center of gravity height (studs relative to median of all wheels)
Tune.WBVisible		= false		-- Makes the weight brick visible
--function Typewrite(Dialouge)
--	for i= 1, Dialouge:len() do
--		textbox.Text = textbox.Text..Dialouge:sub(i,i)
--		wait(.01)
--	end
--	-- You can change the speed of the typing and the delay(I made it adaptive to the length of the text)
--	wait(Dialouge:len()/25)
--	for i= 1, Dialouge:len() do
--		textbox.Text = Dialouge:sub(1,Dialouge:len()-i)
--		wait(.01)
--	end
--end
-------- OMG HAX



r = game:service("RunService")


Tool = script.Parent

local equalizingForce = 236 / 1.2 -- amount of force required to levitate a mass
local gravity = .90 -- things float at > 1

local ghostEffect = nil
local massCon1 = nil
local massCon2 = nil

function recursiveGetLift(node)
	local m = 0
	local c = node:GetChildren()
	for i=1,#c do
		if c[i].className == "Part" then
			if c[i].Name == "Handle" then
				m = m + (c[i]:GetMass() * equalizingForce * 1) -- hack that makes hats weightless, so different hats don't change your jump height
			else
				m = m + (c[i]:GetMass() * equalizingForce * gravity)
			end
		end
		m = m + recursiveGetLift(c[i])
	end
	return m
end


function onMassChanged(child, char)
	print("Mass changed:" .. child.Name .. "    " .. char.Name)
	if (ghostEffect ~= nil) then
		ghostEffect.force = Vector3.new(0, recursiveGetLift(char) ,0)
	end
end



function UpdateGhostState(isUnequipping)

	if isUnequipping == true then
		ghostEffect:Remove()
		ghostEffect = nil
		massCon1:disconnect()
		massCon2:disconnect()
	else
		if ghostEffect == nil then
			local char = Tool.Parent
			if char == nil then return end
			ghostEffect = Instance.new("BodyForce")
			ghostEffect.Name = "GravityCoilEffect"
			ghostEffect.force = Vector3.new(0, recursiveGetLift(char) ,0)
			ghostEffect.Parent = char.Head
			ghostChar = char
			massCon1 = char.ChildAdded:connect(function(child)  onMassChanged(child, char) end)
			massCon2 = char.ChildRemoved:connect(function(child)  onMassChanged(child, char) end)
		end
	end
	

end



function onEquipped()
	Tool.Handle.CoilSound:Play()
	UpdateGhostState(false)
end

function onUnequipped()
	UpdateGhostState(true)
end


script.Parent.Equipped:connect(onEquipped)
script.Parent.Unequipped:connect(onUnequipped)
-- create a proxy object for a table that prevents reading
-- empty keys, writing keys, and inspecting the metatable


local tableLock do
	local function invalidRead(_, k)
		error(("reading nonexistent element %q from locked table"):format(tostring(k)), 2)
	end

	local function invalidWrite(_, k)
		error(("writing key %q to locked table"):format(tostring(k)), 2)
	end

	local RW_LOCK = {
		__index = invalidRead,
		__newindex = invalidWrite,
	}

	function tableLock(tbl)
		setmetatable(tbl, RW_LOCK)

		return setmetatable(
			{},
			{
				__index = tbl,
				__newindex = invalidWrite,
				__metatable = "The metatable is locked",
			}
		)
	end
end