Ore Extractor Feedback

I’ve decided to remake discontinued game to learn to code.
I’ve made a scanning tool. If you press and hold left mouse, a beam is created from the nozzle to the object. If the object is an NPC, a gui appears displaying its HP, name and aggression (Hostile, etc).
If it’s an ore deposit, after scanning it for long enough you obtain ore. A block in the deposit is then destroyed.

I wish this code to be more efficient. It would be great if it could work on mobile too somehow.
A known improvement is not firing an event which is detected by the same script, although I feel it may require a coroutine and my attempts thus far at learning to use it elsewhere have failed miserably.
I have a localscript in the tool which detects if its Activated and sends the mouse position and player through an event to a server script inside the tool which handles the rest.
A known bug is, for some reason, after collecting one ore a 2nd ore quickly gets extracted with it.
Concider that multiple players may choose to extract from the same deposit, and that deposits have limited ore.

Local:

local mouse = game.Players.LocalPlayer:GetMouse()
local joe = false

script.Parent.Activated:Connect(function()
	joe = true
	while joe == true do
		script.Parent.Fire:FireServer(mouse.Hit.Position)
		wait(.01)
	end
end)

script.Parent.Deactivated:Connect(function()
	joe = false
	script.Parent.Fire:FireServer()
end)

Server:

local SS = game:GetService("ServerStorage")
local SSS = game:GetService("ServerScriptService")
local RS = game:GetService("ReplicatedStorage")

local tool = script.Parent
local shoot_part = tool:WaitForChild("Shoot")

local playerData = require(SSS.PlayerData)
local guiEvent = RS.RemoteEvents.ScanGui

local lastScan = nil
local currentScan = nil
local scanning = false

script.Parent.Fire.OnServerEvent:Connect(function(player,mousePos) --Makes raycast
	print("hi")
	if mousePos then
		local rayParam = RaycastParams.new() --Blacklists player model/decendants
		rayParam.FilterDescendantsInstances = {player.Character}
		rayParam.FilterType = Enum.RaycastFilterType.Blacklist
		
		local origin = shoot_part.Position --Setting up and firing raycast
		local direction = (mousePos - origin).Unit
		local result = workspace:Raycast(shoot_part.Position, direction*30,rayParam)
		if result then
			
			if result.Instance.Parent ~= lastScan then --Checks if no longer scanning the same thing
				lastScan = result.Instance.Parent
				scanning = false
			end
			
			if result.Instance.Parent:FindFirstChild("Scannable") and scanning == false then 
				script.Parent.Scan:Fire(player,result,lastScan)
				--Fires event to scan if not already scanning, and if thing is scannable
			end
			
			local pre = workspace.Terrain:FindFirstChild(player.Name)
			--checks if attatchment exists for the beam to end at
			if pre then
				pre.Position = result.Position --moves attatchment to scanning location
			else
				local attachment = Instance.new("Attachment") --makes new attacthment if no attatchment
				attachment.Parent=workspace.Terrain
				attachment.Name=player.Name
				attachment.Position = result.Position
				script.Parent.Beam1.Attachment1 = attachment
				script.Parent.Beam2.Attachment1 = attachment
			end
		else --if raycast didnt find anything, destroys attatchment(ends scan beam)
			local jane = workspace.Terrain:FindFirstChild(player.Name)
			
			if jane then
				jane:Destroy()
			end
		end
	else
		scanning = false
		local jane = workspace.Terrain:FindFirstChild(player.Name)
		if jane then
			jane:Destroy()
		end
	end
end)

script.Parent.Scan.Event:Connect(function(player,result,lScan) --Begins the extraction process
	scanning = true
	--Checking for the strValue/type of object scanned
	local Type = result.Instance.Parent.Scannable:FindFirstChild("Type")
	
	if Type and Type.Value == "NPC" and result.Instance.Parent.Humanoid.Health > 0 then
		player.PlayerGui.ScanGui.NPCGui.BG.HPBG.Humanoid.Value = result.Instance.Parent.Humanoid
		guiEvent:FireClient(player,result.Instance.Parent.Name) --Displays details of the npc as a Gui
	else --if its ore thats scanned, give ore if scanning for long enough
		local ore = result.Instance.Parent
		local element = ore.Scannable.Value --what ore is it? strValue
		local x = 0
		while result.Instance.Parent == lScan and scanning == true do
			x+= 1
			if x == 3 then
				scanning = false
				if  ore.Remain.Value > 0 then
					ore:FindFirstChild(element..ore.Remain.Value):Destroy()
					ore.Remain.Value -=1
					SS.giveXp:Fire(ore.Exp.Value, player)
					playerData.upData(player,"items",element,1,0) --modulescript which handles data
				end
			else
				wait(1)
			end
		end
	end
end)