How to differentiate between a tool being equipped or 'swapped'

I basically need a way to determine if a tool was equipped or swapped, the only way I can think to do this would be to record the time difference between the removal of one and addition of the other?

The purpose is certain tools will behave differently when swapped to another as opposed to de-equipping the tool all together and equipping the other one. There’s an extra step in the middle which will change the behaviour.

Thanks

The default Tool class does not have this function, and implementing it can be very hacky that it’s probably better to work with a custom (or backpack) implementation.

Anyways, I’ve made a server Script to implement a hacky method with explanations in the code itself. I tested this with a LocalScript and behaves the same way, but I have only tested this on studio, not on a live server:

--!strict

-- Equipped method by keeping track of the number of tools
-- in our backpack and comparing it whenever we equip the tool
if script.Parent.Parent.Parent.ClassName == "Player" then
	local player: Player = script.Parent.Parent.Parent
	local backpack: Backpack = player:FindFirstChildOfClass("Backpack") :: Backpack
	local numberOfTools: number = 0
	
	-- function to count the number of tools in our backpack
	local function countTools(): number
		local total: number = 0
		
		for _: number, instance: Instance in backpack:GetChildren() do
			if instance.ClassName == "Tool" then
				total += 1
			end
		end
		
		return total
	end
	
	-- function to update the number of tools in our backpack
	local function updateNumberOfTools(): ()
		-- we defer it so when Equipped gets fired, we can get the
		-- previously number of tools and compare it to the new one
		task.defer(function(): ()
			numberOfTools = countTools()
		end)
	end
	
	-- update the number of tools and hook it onto our backpack
	-- listener for ChildAdded and ChildRemoving
	updateNumberOfTools()
	
	backpack.ChildAdded:Connect(updateNumberOfTools)
	backpack.ChildRemoved:Connect(updateNumberOfTools)
	
	-- Equipped event listener
	script.Parent.Equipped:Connect(function(): ()
		-- get the number of tools before the Equipped event
		local toolCountBeforeEquipped: number = numberOfTools
		
		-- we compare of the number of tools before the Equipped event
		-- minus 1 (since this tool moved itself out of the backpack)
		-- to the new number of tools in our backpack. If it does not
		-- match, that means we equipped normally and no previous tool
		-- went back into our backpack. if it matches, that means a
		-- previous tool has been added back into the backpack via
		-- unequpping
		if toolCountBeforeEquipped - 1 == countTools() then
			print("Equipped normally")
		else
			print("Swapped into")
		end
	end)
end

-- Unequipped method by checking if a new tool has been equipped
-- when we uneqipped this tool
script.Parent.Unequipped:Connect(function(): ()
	if script.Parent.Parent.Parent.ClassName == "Player" then
		local character: Model? = (script.Parent.Parent.Parent :: Player).Character
		local tool: Tool? = if character ~= nil then character:FindFirstChildOfClass("Tool") else nil

		if tool ~= nil then
			print("Swapped away")
		else
			print("Unequipped normally")
		end
	end
end)

Do note that in the end, if you’re trying to do something like swapping out animations, this method will not be possible unless you have a custom model outside of your Tool that is animating since it immediately goes back into your backpack upon unequipping, which at that point it would serve you well if you just made your own tool and/or backpack implementation.

local PreviousTool = ""

game.Players.LocalPlayer.Character.ChildAdded:Connect(function(Child)
    if not Child:IsA("Tool") then return end
    if Child.Name == PreviousTool then
        -- player equipped the same tool
    elseif PreviousTool ~= "" then
        -- player equipped a different tool
        if game.Players.LocalPlayer.Backpack:FindFirstChild(PreviousTool) then
           -- player most likely swapped tools
        else 
           -- player most likely dropped the previous tool
        end
    end; PreviousTool = Child.Name
end)