Tycoon Kit Having Issues with .TouchedEvents

I have a tycoon kit that I made myself that is having some issues recently, specifically when players attempt to buy buttons or collect cash.

It is not a consistent glitch. Some people, like one of my developers, experience it frequently, and many others, like myself have never experienced it, period. This makes it something that is pretty difficult to replicate.

There is no information or errors given to me by output/the developer console when this issue occurs.

First and foremost, I have tycoons structure in an OOP-style structure, where a server script places the tycoon model/setup around the base, and then executes a tycoon.new() constructor to actually get them working.

I also use Janitor to cleanup connections in the tycoon when it is destroyed by either the player leaving or rebirthing (and then a new tycoon.new() constructor is called). Before I didn’t use a Janitor, which wasn’t good practice given the connections I am running, but it seems that using Janitor may have caused more issues with the button buying/cash collecting.

Second, I have a bit of a weird setup for the cash collectors. There is one main cash collector model, and in it are two collectors. I have the following code for handling that

for _,child in next,self.collection:GetChildren() do
		if child:FindFirstChild("main") ~= nil then
			self._janitor:Add(
				child.main.Touched:Connect(function(hit)
					local tP = game.Players:GetPlayerFromCharacter(hit.Parent)
					if tP ~= nil then
						if tP == self.values.owner.Value then
							if self.autoCollect ~= true and self.values.debounce.Value == false and self.values.cashToCollect.Value > 0 then
								child.main.BrickColor = BrickColor.new("Bright red")
								self.values.debounce.Value = true
								local currAmount = self.values.cashToCollect.Value
								if tP.multiplier.Value >= 2 then
									local newCurrAmount = currAmount*2
									tP.leaderstats.Cash.Value = tP.leaderstats.Cash.Value + newCurrAmount
									--self.values.cashToCollect.Value = self.values.cashToCollect.Value - currAmount
									self.values.cashToCollect.Value = 0
									notificationRemote:FireClient(tP,
										"Income Notification",
										"Since you own the Double Income game pass, you've collected an increased $"..newCurrAmount.."!")
								else
									tP.leaderstats.Cash.Value = tP.leaderstats.Cash.Value + currAmount
									--self.values.cashToCollect.Value = self.values.cashToCollect.Value - currAmount
									self.values.cashToCollect.Value = 0
									notificationRemote:FireClient(tP,"Income Notification","You've collected $"..currAmount.."!")
								end
								task.wait(2)
								child.main.BrickColor = BrickColor.new("Bright blue")
								self.values.debounce.Value = false
							end
						end
					end	
				end), "Disconnect")
		end
end

Here is a snippet of the code that handles buttons. In this code, when a new child is added to the buttons to buy folder, which is detected by a .ChildAdded connection, it establishes a connection for that button for when that button is stepped on:

self._janitor:Add(
		self.objects.buttons.ChildAdded:Connect(function(Button)
			task.wait(.1)
			if Button == nil then error("button error!") end
			local ButtonHead = Button:WaitForChild("Head",3) 
			local PriceText = ""
			local stats = Button:WaitForChild("stats",3)
			if stats ~= nil then
				local itemName = stats.buys.Value.Name
				if Button:GetAttribute("buyType") == "Cash" then ---- if the button is bought using cash...
					if Button:GetAttribute("tier") ~= nil and Button:GetAttribute("tierMulti") ~= nil then
						local rebirthMulti = 0
						if self.values.owner.Value ~= nil then
							--rebirthMulti = rebirthMulti + self.values.owner.Value.leaderstats.Rebirths.Value
						end
						local tier = Button:GetAttribute("tier")
						local tierMulti = Button:GetAttribute("tierMulti")
						local price = math.ceil((costFinder.getTierAmount(tier)*math.max(tierMulti,1))*(1+(.01*rebirthMulti)))
						Button:SetAttribute("cost",price)
						task.wait(.01)
					end
					PriceText = "$"..Button:GetAttribute("cost")
				elseif Button:GetAttribute("buyType") == "gamePass" then ---- bought with robux
					local assetInfo = mkt:GetProductInfo(Button:GetAttribute("cost"),2)
					PriceText = "R$"..assetInfo.PriceInRobux
				elseif Button:GetAttribute("buyType") == "Rebirths" then ---- bought with rebirth
					PriceText = Button:GetAttribute("cost").." Rebirth(s)"
				else
					PriceText = "Premium Members Only"
				end;
				Button.display.item.Text = "Buy "..itemName
				Button.display.price.Text = PriceText;
				task.wait()
				ButtonHead.Touched:Connect(function(hit)
						if self.values.debounce.Value == false then
							self.values.debounce.Value = true
							local player = Players:GetPlayerFromCharacter(hit.Parent); 
							if player ~= nil then
								if player == self.values.owner.Value then
									self:buy(self.values.owner.Value, Button, Button:GetAttribute("cost"), false)
								elseif Button:GetAttribute("teamBuy") == true then
									if player.Team == self.values.owner.Value.Team then
										self:buy(self.values.owner.Value, Button, Button:GetAttribute("cost"), false)
									else
										notificationRemote:FireClient(player,
											"Purchase Failure",
											"You are not a member of the owner's team!");
									end
								else
									notificationRemote:FireClient(player,
										"Purchase Failure",
										"You are not the Commander of this Base!")
								end
							end
							task.wait(1.5)
							self.values.debounce.Value = false
						end
				end)
			end	
		end), "Disconnect")

The weird thing about the buy buttons is, if the owner tries stepping on the button with this glitch, nothing happens, but if I am to step on their button, it gives me the notification that I am not the owner and therefore cannot buy this button.

And finally, here is the constructor:

function tycoons.new(model)
	local self = setmetatable({
		_janitor = Janitor.new(), --- the janitor
		actualModel = model,
		events = model:WaitForChild("events",3), --- folder that contains RemoteEvent
		objects = model:WaitForChild("objects",3), --- folder that contains bought objects + buttons
		values = model:WaitForChild("values",3), --- this contains the values like owner of the tycoon etc
		collection = model:WaitForChild("collectorMoney",3), --- this is the collector cash model
	},tycoons)