Sending a function to a modulescript then running it inside of the modulescript is causing random problems?

i work for a tower defense game and im sending towers attack function to a modulescript, and then using :Attack to run the function inside of the modulescript

this has for some reason causes problems with how the towers work

i think it has maybe a reason to do with local functions or local values being sent to the modulescript, but it seems to work with that type of stuff, I HAVE NO IDEA WHAT CAUSES THIS.

this is the script, :AddAttackFunction adds the function, and at the bottom, :Attack essentially runs the function

tower:AddAttackFunction(function(target)
	local flighttime = tower:GetFlightTime("Flight_Speed", target)
	tower:FireClient("shoot", target, flighttime)

	tower:ForecastDamage(target, tower:GetStat("Damage"), flighttime)
	tower:Wait(flighttime)
	tower:Damage(target, tower:GetStat("Damage"))
end)

while true do
	while souls.Value <= 0 do
		souls.Changed:Wait()
	end
	
	local times = tower:Cooldown()
	local enemies = tower:WaitForEnemiesInRange(nil, true)
	local savedamt = souls.Value
	changesouls(-1)
	
	local target = tower:GetTarget(enemies)

	tower:Attack(times, target)
end

this is the modulescript, both functions

	tower.AttackFunctions = {}

	function tower:AddAttackFunction(func, specificname)
		if specificname ~= nil then
			assert(type(specificname) == "string", "Invalid function name type")
		end

		local funcname = specificname or tostring(#self.AttackFunctions + 1)
		self.AttackFunctions[funcname] = func

		return func
	end

	function tower:Attack(times, target, specific)
		
		if target == true then
			target = self:GetTarget(self:GetEnemiesInRange())

			if target == nil then
				error("Invalid target provided to: " .. self.Object.Name)
			end
		end

		local function runfunc(func)
			for x = 1, times or 1 do
				self:TrueRun(func, target)
			end
		end

		if specific then
			if self.AttackFunctions [specific] == nil then
				error(self.Object.Name.." does not have specific function: "..specific..", cannot force attack")
			end

			runfunc(self.AttackFunctions [specific])
		else
			for _, func in self.AttackFunctions do
				runfunc(func)
			end
		end
	end

the fact is that if i just replace tower:Attack with the lines in the function, like this:

while true do
	while souls.Value <= 0 do
		souls.Changed:Wait()
	end
	
	local times = tower:Cooldown()
	local enemies = tower:WaitForEnemiesInRange(nil, true)
	local savedamt = souls.Value
	changesouls(-1)
	
	local target = tower:GetTarget(enemies)

--below is me moving the attack function in place of :Attack
	local flighttime = tower:GetFlightTime("Flight_Speed", target)
	tower:FireClient("shoot", target, flighttime)

	tower:ForecastDamage(target, tower:GetStat("Damage"), flighttime)
	tower:Wait(flighttime)
	tower:Damage(target, tower:GetStat("Damage"))
end

IT WORKS JUST FINE, I cannot find out what causes this and I need help, please provide any help if you figure out what causes this, thank you.

there are no errors, and there are just random things that happens that are small but large problems at the same time.

also if you want me to send videos of the differences, i can.

Come on lucky guessing! 1st attempt…

tower:AddAttackFunction(function(target)
	local flighttime = tower:GetFlightTime("Flight_Speed", target)
	tower:FireClient("shoot", target, flighttime)
	tower:ForecastDamage(target, tower:GetStat("Damage"), flighttime)
	tower:Wait(flighttime)
	tower:Damage(target, tower:GetStat("Damage"))
end)

function tower:Attack(times, target, specific)
	local function runfunc(func)
		for x = 1, times or 1 do
			func(target) -- directly passed
		end
	end

	if specific then
		runfunc(self.AttackFunctions[specific])
	else
		for _, func in self.AttackFunctions do
			runfunc(func)
		end
	end
end

:thinking: 2nd attempt…

tower:AddAttackFunction(function(towerRef, target) -- towerRef passing explicit
	local flighttime = towerRef:GetFlightTime("Flight_Speed", target)
	towerRef:FireClient("shoot", target, flighttime)
	towerRef:ForecastDamage(target, towerRef:GetStat("Damage"), flighttime)
	towerRef:Wait(flighttime)
	towerRef:Damage(target, towerRef:GetStat("Damage"))
end)

while true do
	while souls.Value <= 0 do
		souls.Changed:Wait()
	end
	
	local times = tower:Cooldown()
	local enemies = tower:WaitForEnemiesInRange(nil, true)
	local savedamt = souls.Value
	changesouls(-1)
	
	local target = tower:GetTarget(enemies)

	tower:Attack(times, target)
end

----

tower.AttackFunctions = {}

function tower:AddAttackFunction(func, specificname)
	local funcname = specificname or tostring(#self.AttackFunctions + 1)
	self.AttackFunctions[funcname] = func
	return func
end

function tower:Attack(times, target, specific)
	local function runfunc(func)
		for x = 1, times or 1 do
			func(self, target) -- direct self passing explicitly
		end
	end

	if specific then
		runfunc(self.AttackFunctions[specific])
	else
		for _, func in self.AttackFunctions do
			runfunc(func)
		end
	end
end

:unamused: … time to change up.

--Looking to isolate the problem

tower:AddAttackFunction(function(target)
    print("Attack function called with target:", target)
end)

while true do
    while souls.Value <= 0 do
        souls.Changed:Wait()
    end
    
    local times = tower:Cooldown()
    local enemies = tower:WaitForEnemiesInRange(nil, true)
    local savedamt = souls.Value
    changesouls(-1)
    
    local target = tower:GetTarget(enemies)

    tower:Attack(times, target)
end


--ModuleScript


tower.AttackFunctions = {}

function tower:AddAttackFunction(func, specificname)
    if specificname ~= nil then
        assert(type(specificname) == "string", "Invalid function name type")
    end

    local funcname = specificname or tostring(#self.AttackFunctions + 1)
    self.AttackFunctions[funcname] = func

    return func
end

function tower:Attack(times, target, specific)
    local function runfunc(func)
        for x = 1, times or 1 do
            print("Running function for target:", target)
            func(target)
        end
    end

    if specific then
        runfunc(self.AttackFunctions[specific])
    else
        for _, func in self.AttackFunctions do
            runfunc(func)
        end
    end
end

--output: Check your PMs for a breakdown of outputs 
--because things got complicated..

I’m done guessing … this should tell you where the problem is.
Good luck!

it does not work, this is very similar to how it already is, thank you for trying though

the second solution does not work either, thank you again for trying

Come on man, you gotta give us more than “causing random problems” but say “it works just fine… there are no errors.”

I would highly recommend debugging. Add prints, watch variables, use breakpoints, create a reproduction scendario - utilize them all.

Should only be 1 of 4 possible problems. Just tested out 2 of them.
The debug script set up should test Reference Passing.
That leaves timing: how the function is invoked through the module.
Really hope it’s not that one. Let’s hope the Passing testing shows it.

Timing shouldn’t matter since nothing seems to be running asynchronously here, nor should it matter anyways. Pretty sure the main issue here is that Attack takes account of the times variable, but his new script does not and should be ran in a for loop as well.

This is, of course, assuming his original script does not have any unexpected behaviors.

I try to keep as close as possible to the scripts posted.
My last post was a test. Showing outputs that should tell the tale.

Unfortunately timing would explain exactly what he is saying is happening.
Odd what the heck moments with no errors usually mean timing.

Everything I say is a bit off. Unless it works, then I was totally serious.

the solution was a different function, the :Cooldown function seemed to become different due to this change in function, it has been fixed, thank you all for your help.

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