My automatic tool activation script goes faster then usual. Is there another way to do it so it works properly?

Basically, the tool has a MouseButton1Down that sets a variable Firing to true, and Button1Up sets it to false.
Because it is initially false, I can’t use

while Firing do
       Fire()
       wait(0.073)
end

or

while true do
      if Firing then Fire(); wait(0.073); end
end

That crashes the game. So i’m stuck with using

while true do
      if Firing then Fire(); wait(0.073); end
      game.RunService.Heartbeat:Wait() -- renamed RunService
end

Which produces uneven results. So is there a better way to detect when mouse is down, fire, wait, and repeat until the mouse is lifted?

Instead, upon activation on mouse, start the loop and the loop will exit as soon a state changes where the mouse has stopped its primary button down. This way would avoid having an unnecessary loop running forever. RunService would be in place of this, since they usually house the fastest waiting time and responds as accordingly without interfering with the renderer(frames aren’t suffering too much, except from code expense per loop). To run this timer down, simply try something with os.clock().

local ongoingTick = os.clock()
repeat -- optionally a while loop can work
    RunService.Heartbeat:Wait()
until os.clock() - ongoingTick >= timeBeforeNewIteration

You can have the loop run as a connected function to the mouse event, which would run in conjunction with a debounce variable (along with the button up event). For example, depending on your code, you’ll probably have both of them in a function connected to your event of the tool. Which would work for nearly any problem of a similar nature, be it related to mouse events or not. Like this:

-- You can have the mouse button events wherever you want eitherway, depending on your code
local Firing = false

-- The event gets Mouse as the parameter
Tool.Equipped:Connect(function(mouse)
	mouse.Button1Down:Connect(function()
		if not Firing then
		    Firing = true --Setting it to true so it won't have 2 different loops or more
		    while Firing do
		        Fire()
		        -- Any other code you want
                wait(0.073)
            end
        end
	end)
	
	mouse.Button1Up:Connect(function()
    	if Firing then
    	    Firing = false
    	end
    end)
end)

Notice: If you were to get the mouse object without having it already as a parameter, you’d use the GetMouse() function on the LocalPlayer from the Players service.

Let me try formatting that code to see if it makes sense.