Bug that I'm trying to fix from several hours

I’m currently making a game about chopping trees with axes. Everything works except one annoying bug that I can’t fix from several hours. I wanted to ask someone more experienced. The problem is that when timing right the [layer can start chopping several trees at once. Obviously that’s not the point but I think I tried everything to fix it. I tried with chat gpt and grok, they solved it but they changed the whole script which I don’t want because I want to understand everything in the script. That’s the reason why I’m here.

Anyways here a video of the problem:

And here is the main code which is the cause of the problem:

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local hrp = character:WaitForChild("HumanoidRootPart")

local axe = game.ReplicatedStorage.Axe:Clone()
axe.Parent = workspace

local Parts = axe:GetChildren()
local targetCFrame
local Goals = {}

local TweenService = game:GetService("TweenService")

local TweenInfoData = TweenInfo.new(
	0.8,
	Enum.EasingStyle.Cubic,
	Enum.EasingDirection.Out,
	0,
	false
)

local TweenInfoBarData = TweenInfo.new(
	0.2,
	Enum.EasingStyle.Sine,
	Enum.EasingDirection.Out,
	0,
	false
)

local TweenInfoChop = TweenInfo.new(
	0.2,
	Enum.EasingStyle.Cubic,
	Enum.EasingDirection.InOut,
	0,
	false
)

local leaderstats = player:WaitForChild("leaderstats")
local Coins = leaderstats:WaitForChild("Coins")
local Wood = leaderstats:WaitForChild("Wood")
local SpecialWood = leaderstats:WaitForChild("Special Wood")

local Trees = game.Workspace.Trees:GetChildren()
local x = -2
local Following = true
local MovementTween
local Clicked = false
local oldtree
local chopping = false


local choppingTime = 1 -- can be changed
local damage = 5 -- can be changed

for i, tree in pairs(Trees) do
	local ClickDetector = tree:FindFirstChild("ClickDetector")
	local OriginalHealth = tree:FindFirstChild("Health"):FindFirstChild("Health").Value
	local HealthGui = tree:FindFirstChild("Health")
	HealthGui.Enabled = false

	local function chop(tree)
		--print("start")
		HealthGui.Enabled = true
		--print("chop")
		local Health = HealthGui:FindFirstChild("Health")
		local Frame = HealthGui:FindFirstChild("Frame")
		local Bar = Frame:FindFirstChild("Bar")
		local TextLabel = Frame:FindFirstChild("Text")

		game.ReplicatedStorage.TakeDamage:FireServer(Health, damage)
		--Health.Value -= damage
		local BarGoals = { Size = UDim2.new((Health.Value - damage) / OriginalHealth, 0, 1, 0) }
		local ChageSizeTween = TweenService:Create(Bar, TweenInfoBarData, BarGoals)
		ChageSizeTween:Play()
		TextLabel.Text = Health.Value - damage .. "/" .. OriginalHealth

		if Health.Value - damage <= 0 then
			Bar.Size = UDim2.new(0, 0, 1, 0)
			TextLabel.Text = "0/".. OriginalHealth
			--SpecialWood.Value += 1
			print("yay")
			local Event = game.ReplicatedStorage.DestroyedTree
			Event:FireServer(tree)
			chopping = false
			Following = true
		end

		game.ReplicatedStorage.Chop:FireServer()

		--[[Coins.Value += math.random(1,5)
		Wood.Value += math.random(1,3)]]

		task.wait(1)
		--print("end")
	end
	
	local function onChopping(tree)
		while true do
			if chopping == true then
				print("tui")
				chopping = true
				chop(tree)
			else
				break
			end
		end
	end
	ClickDetector.MouseClick:Connect(function()
		if Clicked == false then
			Clicked = true
			print("Clicked")
			targetCFrame = tree.main.CFrame * CFrame.new(0, 0, 3.5)

			task.wait(choppingTime)
		end
	end)

	local function onClicked(tree)
		print(tree)
		Clicked = not Clicked


		if tree == oldtree then
			print("oh no")
			--Following = true
		else
			chopping = false
			oldtree = tree
			Following = false

			for _, part in pairs(Parts) do
				--print(part.Name)
				if part.Name == "top" then
					local targetCFrame = tree.main.CFrame * CFrame.new(-1.35, x, 5.95) * CFrame.Angles(0, 0, math.rad(90))
					Goals = { CFrame = targetCFrame }
				elseif part.Name == "middle" then
					local targetCFrame = tree.main.CFrame * CFrame.new(0, x, 6.5) * CFrame.Angles(0, 0, math.rad(90))
					Goals = { CFrame = targetCFrame }
				else
					local targetCFrame = tree.main.CFrame * CFrame.new(0.95, x, 6.575) * CFrame.Angles(0, 0, math.rad(90))
					Goals = { CFrame = targetCFrame }
				end

				MovementTween = TweenService:Create(part, TweenInfoData, Goals)
				MovementTween:Play()
			end

			MovementTween.Completed:Wait()
			
			if chopping == true then
				print("bad")
			end
			chopping = true
			onChopping(tree)
			print(tree, oldtree)
		end
	end



	ClickDetector.MouseClick:Connect(function()
		onClicked(ClickDetector.Parent)
	end)
end



while true do
	wait()
	if Following == true and chopping == false then
		for _, part in pairs(Parts) do
			--print(part.Name)
			if part.Name == "top" then
				local targetCFrame = hrp.CFrame * CFrame.new(0, x + 2.844, 3.5)
				Goals = { CFrame = targetCFrame }
			elseif  part.Name == "middle" then
				local targetCFrame = hrp.CFrame * CFrame.new(0, x + 1.445, 3.5)
				Goals = { CFrame = targetCFrame }
			else
				local targetCFrame = hrp.CFrame * CFrame.new(0, x + 0.419, 3.5)
				Goals = { CFrame = targetCFrame }
			end

			MovementTween = TweenService:Create(part, TweenInfoData, Goals)
			MovementTween:Play()
		end
		task.wait(0.15)
	end
end

Thanks a lot for the help, I fell like I tried everything.

2 Likes

Looks like you are iterating through all your trees and chopping every one in the list.
You may want to use a tag to choose a single tree then use a different function to check if that tree is the one the player has selected and cut just that tree.

3 Likes

I use this to set up the events in the memory. I’m not chopping everything on one time. It’s working but only one bug is causing this to happen but not every time. Also About the tags like everything else it seems to work but actually doesn’t.

1 Like

try storing a connection of the tweens in a dictionary and cancel out previous tweens when moving to a new tree.

1 Like

The problem is not the movement and the tweens but the chopping part and dealing damage of the tree.

1 Like

You need two things to avoid this.

1 - Store the object/humanoid that will suffer the damage.
2 - Update a value (number) every time the change from one target to another is called.

To avoid problems with loops, you need checks. Always study the best checks to avoid storing too many things when they can be solved more easily. In your case, you only need to check at each start of the loop if the target and the value are the same.

Tip: as a value for the second variable, you can use math.floor(workspace:GetServerTimeNow()*1000)

2 Likes

yeah thats what i meant the tweens are still playing and the damage has been done.

1 Like

Hi @DinoKris30 can you try this:

Issue may occure when chopping is already true and while loop will not break unless chopping tree is stopped that why you are facing this error

when you swich to another tree chopping is still true then old loop will not stop

Original:

local function onChopping(tree)
	while true do
		if chopping == true then
			print("tui")
			chopping = true
			chop(tree)
		else
			break
		end
	end
end

New:

local function onChopping(tree)

	while chopping and oldtree == tree do
		chop(tree)
	end
end

I added oldtree == true to stop the old loop when you swich to new

if you need i can give you full code with changes.

2 Likes

Here is full modified script:

-- Services --
local TweenService = game:GetService("TweenService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

local player = Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local hrp = character:WaitForChild("HumanoidRootPart")

local leaderstats = player:WaitForChild("leaderstats")

-- List of leaderstats --
local Coins = leaderstats:WaitForChild("Coins")
local Wood = leaderstats:WaitForChild("Wood")
local SpecialWood = leaderstats:WaitForChild("Special Wood")

-- Handle Axe Cloning --
local axe = ReplicatedStorage.Axe:Clone()
axe.Parent = workspace

local Parts = axe:GetChildren() -- Gets All parts of axe

local targetCFrame

local Goals = {}

local Trees = workspace.Trees:GetChildren() -- Geting all trees inside workspace.Trees

local x = -2
local choppingTime = 1 -- Time of choping wood
local damage = 5 -- Damage dealt to wood

local oldtree = nil

local Following = true
local chopping = false

-- Remote Events
local TakeDamageEvent = ReplicatedStorage:FindFirstChild("TakeDamage") 
local DestroyedTreeEvent = ReplicatedStorage:FindFirstChild("DestroyedTree")
local ChopEvent = ReplicatedStorage:FindFirstChild("Chop")

local TweenInfoData = TweenInfo.new(
	0.8,
	Enum.EasingStyle.Cubic,
	Enum.EasingDirection.Out,
	0,
	false
)

local TweenInfoBarData = TweenInfo.new(
	0.2,
	Enum.EasingStyle.Sine,
	Enum.EasingDirection.Out,
	0,
	false
)

local TweenInfoChop = TweenInfo.new(
	0.2,
	Enum.EasingStyle.Cubic,
	Enum.EasingDirection.InOut,
	0,
	false
)

for i, tree in pairs(Trees) do
	
	local ClickDetector = tree:FindFirstChild("ClickDetector")
	local HealthGui = tree:FindFirstChild("Health")
	local Health = HealthGui:FindFirstChild("Health")
	local OriginalHealth = Health.Value

	HealthGui.Enabled = false

	local function chop()
		if not Health then return end

		HealthGui.Enabled = true

		local Frame = HealthGui:FindFirstChild("Frame")
		local Bar = Frame:FindFirstChild("Bar")
		local TextLabel = Frame:FindFirstChild("Text")

		TakeDamageEvent:FireServer(Health, damage)
		Health.Value = math.max(Health.Value - damage, 0) -- Ensure it doesn't go negative

		if Bar then
			local BarGoals = { Size = UDim2.new(Health.Value / OriginalHealth, 0, 1, 0) }
			local ChangeSizeTween = TweenService:Create(Bar, TweenInfoBarData, BarGoals)
			ChangeSizeTween:Play()
		end

		if TextLabel then
			TextLabel.Text = Health.Value .. "/" .. OriginalHealth
		end

		if Health.Value <= 0 then
			if Bar then Bar.Size = UDim2.new(0, 0, 1, 0) end
			if TextLabel then TextLabel.Text = "0/" .. OriginalHealth end

			DestroyedTreeEvent:FireServer(tree)
			chopping = false
			Following = true
		end

		ChopEvent:FireServer()
		task.wait(choppingTime)
	end

	local function onChopping()
		
		while chopping and oldtree == tree do
			chop()
		end
	end

	local function onClicked()

		if tree ~= oldtree then
			
			oldtree = tree
			
			chopping = false
			Following = false

			for _, part in pairs(Parts) do
				local targetCFrame

				if part.Name == "top" then
					targetCFrame = tree.main.CFrame * CFrame.new(-1.35, 0, 5.95) * CFrame.Angles(0, 0, math.rad(90))
				elseif part.Name == "middle" then
					targetCFrame = tree.main.CFrame * CFrame.new(0, 0, 6.5) * CFrame.Angles(0, 0, math.rad(90))
				else
					targetCFrame = tree.main.CFrame * CFrame.new(0.95, 0, 6.575) * CFrame.Angles(0, 0, math.rad(90))
				end

				local Goals = { CFrame = targetCFrame }
				
				local MovementTween = TweenService:Create(part, TweenInfoData, Goals)
				MovementTween:Play()

				MovementTween.Completed:Wait()
			end

			chopping = true
			onChopping()
		end
	end

	ClickDetector.MouseClick:Connect(onClicked)
end

while true do
	task.wait()
	
	if Following and not chopping then
		
		for _, part in pairs(Parts) do
	
			if part.Name == "top" then
				local targetCFrame = hrp.CFrame * CFrame.new(0, x + 2.844, 3.5)
				Goals = { CFrame = targetCFrame }
			elseif  part.Name == "middle" then
				local targetCFrame = hrp.CFrame * CFrame.new(0, x + 1.445, 3.5)
				Goals = { CFrame = targetCFrame }
			else
				local targetCFrame = hrp.CFrame * CFrame.new(0, x + 0.419, 3.5)
				Goals = { CFrame = targetCFrame }
			end

			MovementTween = TweenService:Create(part, TweenInfoData, Goals)
			MovementTween:Play()
		end
		
		task.wait(0.15)
	end
end

Notes:

  • I cannot guarantee that this will work because I haven’t tested it.
  • I tried to optimize the code and fix your unoptimized functions and variables.
  • I removed some unnecessary variables.
  • When a new tree is added, it won’t be choppable because you haven’t implemented a system to assign functionality to new trees.
  • Exploiting your damage and chopping time variable is very easy—any hacker can modify it.
2 Likes

Alright thanks a lot, I’m gonna test it later and say what happens. Also I can add new trees because my trees are in one tree folder. Also I didn’t understand that with exploiters. How could I fix it?

2 Likes

Also, I forgot to say Instance cannot be directly passed through Remote Event!

Store the trees being chopped in a table, this would allow you to loop through the table, find the current tree and do damage to that tree. I’m on my phone at the moment so can’t give a large snipped of code. I cab later if you want me to.

This is a bit trickery to fix than it would seem to be. They should be able to stop chopping a tree at any point and start chopping a different one… So this will take more than a simple fix. Always hard to guess without something to test with so this may still need some love …

local function chop(tree)
	HealthGui.Enabled = true
	local Health = HealthGui:FindFirstChild("Health")
	local Frame = HealthGui:FindFirstChild("Frame")
	local Bar = Frame:FindFirstChild("Bar")
	local TextLabel = Frame:FindFirstChild("Text")

	if tree == oldtree then  
		game.ReplicatedStorage.TakeDamage:FireServer(Health, damage)  
		local BarGoals = { Size = UDim2.new((Health.Value - damage) / OriginalHealth, 0, 1, 0) }
		local ChageSizeTween = TweenService:Create(Bar, TweenInfoBarData, BarGoals)
		ChageSizeTween:Play()
		TextLabel.Text = Health.Value - damage .. "/" .. OriginalHealth

		if Health.Value - damage <= 0 then
			Bar.Size = UDim2.new(0, 0, 1, 0)
			TextLabel.Text = "0/".. OriginalHealth
			local Event = game.ReplicatedStorage.DestroyedTree
			Event:FireServer(tree)
			chopping = false
			Following = true
		end
	end  

	game.ReplicatedStorage.Chop:FireServer()
	task.wait(1)
end

My goal here is to only damage the tree they are chopping … while still letting them skip around chopping and/or skipping to as many trees as they like. (that would be the tricky part)

1 Like

That worked, thanks a lot for the help! The full optimized new script that you gave to me didn’t work but i wanted to use mine anyways.

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.