Help optimizing a telekinesis scripts/tool

I am trying to optimize a tool script that is called Levitation and Force tool. It’s based off the 4 seasons show Stranger things and I need help. Here’s what I listed everything to optimize the tools.

What I need to add…

  1. Add a Blood decal face to the character wait 2-3 seconds and goes back to the players’ avatar regular face.

Things inside the tool…

  • Inside Levitation tool…
    → Three Remote Events
    → three scripts (client, server, and a module script called Configuration)

  • Inside Force tool…
    → One Remote event
    → three scripts (client, server, and a module script called Configuration)

Levitation tool Client script…

local UserInputService = game:GetService("UserInputService")
local animation = script.Parent.Animation
local localPlayer = Players.LocalPlayer
local char = localPlayer.Character or localPlayer.CharacterAdded:Wait()
local hum = char:WaitForChild("Humanoid")
local tool = script.Parent
local Configuration = require(tool:WaitForChild("Configuration"))
local startForce = tool:WaitForChild("StartForce")
local endForce = tool:WaitForChild("EndForce")
local updateForcePosition = tool:WaitForChild("UpdateForcePosition")
--Constants
local INPUT_HIT_RAY_LENGTH = Configuration.INPUT_HIT_RAY_LENGTH
local TOOL_USE_COOLDOWN = Configuration.TOOL_USE_COOLDOWN
local FORCE_TIME = Configuration.FORCE_TIME
local FORCE_INFLUENCE_DISTANCE = Configuration.FORCE_INFLUENCE_DISTANCE
local PLAYER_EXCLUSIVE = Configuration.PLAYER_EXCLUSIVE
--Misc Vars
local activationDebounce = true
local forcingPlayerPosition = false
local forcing

local function isPlayer(obj)
	if obj == nil then return false end
	if Players:GetPlayerFromCharacter(obj) then
		return true
	elseif Players:GetPlayerFromCharacter(obj.Parent) then
		return true
	elseif Players:GetPlayerFromCharacter(obj.Parent.Parent) then
		return true
	else
		return false
	end
end

local function findInputHit(x, y, length)
	local INPUT_HIT_RAY_IGNORE_LIST = {localPlayer.Character, forcing}
	if length == nil then length = INPUT_HIT_RAY_LENGTH end
	local unitRay = workspace.CurrentCamera:ViewportPointToRay(x, y)
	local ray = Ray.new(unitRay.Origin, unitRay.Direction * length)
	local part, pos = workspace:FindPartOnRayWithIgnoreList(ray, INPUT_HIT_RAY_IGNORE_LIST,true, true)
	return part, pos, ray.Origin, ray.Direction
end

local updateForcePosition = coroutine.create(function()
	while true do
		local timeDegrade = 0.1
		local timePassed = 0
		print(forcingPlayerPosition)
		repeat
			local inputPos = UserInputService:GetMouseLocation()
			local part, pos, origin, direction = findInputHit(inputPos.X, inputPos.Y)
			local characterPosition = localPlayer.Character.PrimaryPart.Position
			local dist
			local finalPos
			
			if pos ~= nil then
				dist = (characterPosition - pos).Magnitude
				if dist > FORCE_INFLUENCE_DISTANCE then
					dist = FORCE_INFLUENCE_DISTANCE
				else 
					finalPos = pos
				end
			end
			
			if finalPos == nil then
			
			if part == nil then
				dist = FORCE_INFLUENCE_DISTANCE
			else
				dist = (characterPosition - pos).Magnitude
				if dist > FORCE_INFLUENCE_DISTANCE then
					dist = FORCE_INFLUENCE_DISTANCE
				end
			end
			local goalPos = CFrame.new(direction * FORCE_INFLUENCE_DISTANCE, origin).Position
			finalPos = CFrame.new(characterPosition, goalPos)
			finalPos = finalPos * CFrame.new(0, 0, -dist)
			finalPos = finalPos.Position
				
			end
			updateForcePosition:FireServer(finalPos)
			
			wait(timeDegrade)
			timePassed = timePassed + timeDegrade
			if timePassed > FORCE_TIME then
				forcingPlayerPosition = false 
			end
		until forcingPlayerPosition == false
		coroutine.yield()
	end
end)

function getDecal()
	local decals = {5818582212}
	for i, child in pairs(script.Parent:GetChildren()) do
		if child.ClassName == "Decal" then
			table.insert(decals, child)
		end
	end
	if decals ~= 0 then
		return decals[math.random(1, #decals)]
	end
	return nil
end

function toolActivationEvent()
	local inputPos = UserInputService:GetMouseLocation()
    local inputHit = findInputHit(inputPos.X, inputPos.Y)
    local animationTrack = hum:LoadAnimation(animation)
	if inputHit == nil then return end
	local isInputPlayer = isPlayer(inputHit)
	if not isInputPlayer and PLAYER_EXCLUSIVE then return end
	
	local head = char:WaitForChild("Head")
	if head ~= nil then
		local face = head:FindFirstChild("face")
		if face.Texture ~= getDecal().Texture then
			face.Texture = getDecal().Texture
			wait(2.5)
			face:Destroy()
		end
	end

	animationTrack:Play()
    hum.Health = hum.Health - 5
	--[[
	Debounce is placed below the code from above to prevent a instance where
	if a player clicks on a part, there would be a unneccessary cooldown
	--]]
	if activationDebounce == false then return end
	activationDebounce = false
	
	if inputHit.Parent:IsA("Model") then
		forcing = inputHit.Parent
	else
		forcing = inputHit
	end
	
	forcingPlayerPosition = true
	startForce:FireServer(inputHit)
	coroutine.resume(updateForcePosition)
	wait(TOOL_USE_COOLDOWN)
	activationDebounce = true
end

function disableForcing()
	forcing = nil
	forcingPlayerPosition = false
end

function toolDeactivatedEvent()
	disableForcing()
	endForce:FireServer()
end

endForce.OnClientEvent:Connect(function()
	disableForcing()
end)

tool.Activated:Connect(toolActivationEvent)
tool.Deactivated:Connect(toolDeactivatedEvent)

Force tool Client script…

local UserInputService = game:GetService("UserInputService")
local localPlayer = Players.LocalPlayer
local tool = script.Parent
local Configuration = require(tool:WaitForChild("Configuration"))
local luanchObject = tool:WaitForChild("LuanchObject")
local whitelist = {"Usename1", "Username2", "Username3"}
local animation = script.Parent.Animation 
-- Player/Character Variables
local player = game.Players.LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()
-- Define Humanoid
local hum = char:WaitForChild("Humanoid")
--Constants
local INPUT_HIT_RAY_LENGTH = Configuration.INPUT_HIT_RAY_LENGTH
local INPUT_HIT_RAY_IGNORE_LIST = {localPlayer.Character}
local TOOL_USE_COOLDOWN = Configuration.TOOL_USE_COOLDOWN
local FORCE_TIME = Configuration.FORCE_TIME
local PLAYER_EXCLUSIVE = Configuration.PLAYER_EXCLUSIVE
--Misc Vars
local activationDebounce = true
local storedInputObject

if whitelist then
	tool:Clone()
end

local function isPlayer(obj)
	if obj == nil then return false end
	if Players:GetPlayerFromCharacter(obj) then
		return true
	elseif Players:GetPlayerFromCharacter(obj.Parent) then
		return true
	elseif Players:GetPlayerFromCharacter(obj.Parent.Parent) then
		return true
	else
		return false
	end
end

local function findInputHit(x, y)
	local unitRay = workspace.CurrentCamera:ViewportPointToRay(x, y)
	local ray = Ray.new(unitRay.Origin, unitRay.Direction * INPUT_HIT_RAY_LENGTH)
	local part, pos = workspace:FindPartOnRayWithIgnoreList(ray, INPUT_HIT_RAY_IGNORE_LIST,true, true)
	return part, pos
end

function getDecal()
	local decals = {5818582212}
	for i, child in pairs(script.Parent:GetChildren()) do
		if child.ClassName == "Decal" then
			table.insert(decals, child)
		end
	end
	if decals ~= 0 then
		return decals[math.random(1, #decals)]
	end
	return nil
end

function toolActivationEvent()
	local inputPos = UserInputService:GetMouseLocation()
	local inputHit = findInputHit(inputPos.X, inputPos.Y)
	local animationTrack = hum:LoadAnimation(animation)
	if inputHit == nil then return end
	local isInputPlayer = isPlayer(inputHit)
	if not isInputPlayer and PLAYER_EXCLUSIVE then return end
	--[[
		Blood Texture will be included with the animation
	--]]
	local head = character:WaitForChild("Head")
	if head ~= nil then
		local face = head:FindFirstChild("face")
		if face.Texture ~= getDecal().Texture then
			face.Texture = getDecal().Texture
			wait(2.5)
			face:Destroy()
		end
	end
	animationTrack:Play()
	hum.Health = hum.Health - 5
	--[[
	Debounce is placed below the code from above to prevent a instance where
	if a player clicks on a part, there would be a unneccessary cooldown
	--]]
	if activationDebounce == false then return end
	activationDebounce = false
	
	luanchObject:FireServer(inputHit)
	
	wait(TOOL_USE_COOLDOWN)
	activationDebounce = true
end

tool.Activated:Connect(toolActivationEvent)

What gives me in the output…

Any solution to fixing this?

1 Like

Make sure there’s actually an Animation in script.Parent, and try using script.Parent:WaitForChild("Animation") to let it load in before getting it.