How to keep Stunning after getting Hit?

Hi I’m currently making my own PvP Combat game and I was wondering if anyone knows how to have the players stay stunned while still being hit multiple times by another player.

I’m using a module script (That is Server-Sided) to perform this task. This is currently what I have written:

local StunModule = {}

function StunModule:Stun(Character:Instance, Duration: number, StunWalkSpeed: number, StunJumpPower: number)
	local Humanoid = Character:WaitForChild("Humanoid")
	
	if Humanoid then
		if Humanoid.Health <= 0 then
			return
		end
	else
		return
	end
	
	local WalkSpeed = Humanoid.WalkSpeed
	local JumpPower = Humanoid.JumpPower
	
	task.spawn(function()

		Humanoid:SetAttribute("Stunned", true)
		Humanoid.WalkSpeed = StunWalkSpeed
		Humanoid.JumpPower = StunJumpPower
		
		wait(Duration) --// This is the issue.

		Humanoid:SetAttribute("Stunned", false)
		Humanoid.WalkSpeed = WalkSpeed
		Humanoid.JumpPower = JumpPower
	end)
end

return StunModule

The stun works, but the issue is that after the player has been stunned once, the wait function will override the next time a player gets stunned again causing the player to be not stunned and get out of a combo.

How do I make it so that the after the player gets stunned again, the task.spawn function restarts from the beginning and makes the player stunned completely, while also ignoring the wait function?

Please help, thanks!

3 Likes

You can utilize a repeat... until loop.

repeat task.wait(1) Duration -= 1 until Duration <= 0 

That way you can just add to the duration, causing a “stacking” effect. You would of course, need to store the duration somewhere else and add to it as a different variable.

4 Likes

can the duration be an attribute?

1 Like

Yes, you could use an attribute to keep track of the duration, you just need to accommodate the code to utilize it. You could utilize the :GetAttributeChangedSignal() function too, if you really wanted to. In that case, you could just check if the time is greater than 0 and set their WalkSpeed accordingly.

2 Likes

How would I do this in a repeat function??

1 Like

I suggest you change it into values instead of attributes,

make a stunned boolvalue and tick numbervalue in it,

whenever you apply the stun set the tick value to tick() of when the stun was applied

and whenver you want to remove the stun just check if tick same as the tick value in stun and remove it smth like this

local tikk = tick()

Stunned.Value = true
Stunned.Tick.Value = tikk


-- stuff here


if Stunned.Tick.Value == tikk then
    Stunned.Value = false
end
2 Likes

I had this problem some time ago and found the solution

local StunModule = {}
local StunTimes = {}

local function SetDefaultHumanoidAttributes(Character : Instance)
	local Humanoid = Character:FindFirstChild("Humanoid")
	
	Humanoid:SetAttribute("WalkSpeed", Humanoid.WalkSpeed)
	Humanoid:SetAttribute("JumpPower", Humanoid.JumpPower)
end

function StunModule:Stun(Character : Model, Duration: number, StunWalkSpeed: number, StunJumpPower: number)
	local Humanoid = Character:FindFirstChild("Humanoid")
	
	if not Humanoid or Humanoid.Health <= 0 then return end
	
	local WalkSpeed = Humanoid:GetAttribute("WalkSpeed")
	local JumpPower = Humanoid:GetAttribute("JumpPower")
	
	if not WalkSpeed or not JumpPower then
		SetDefaultHumanoidAttributes(Character)
		
		WalkSpeed = Humanoid:GetAttribute("WalkSpeed")
		JumpPower = Humanoid:GetAttribute("JumpPower")
	end
	
	StunTimes[Character] = os.clock()
	
	Humanoid:SetAttribute("Stunned", true)
	
	Humanoid.WalkSpeed = StunWalkSpeed
	Humanoid.JumpPower = StunJumpPower
	
	task.delay(Duration, function()
		if os.clock() - StunTimes[Character] >= Duration then
			Humanoid:SetAttribute("Stunned", false)
			
			Humanoid.WalkSpeed = WalkSpeed
			Humanoid.JumpPower = JumpPower
		end
	end)
end

return StunModule
3 Likes

So the problem is that after waiting, we don’t know if the target was stunned again. To do this, you can use newproxy(), which generates a random but unique value each time it is called. It’s not that much more efficient than math.random() or tick(), but it’s the best option.

There’s a lot of limitations to having a loop to check for stun, such as stun having to be a multiple of how often the stun loop checks and task.wait()/wait() being inneffective at very low delays.

Anyways, here’s the code using newproxy()

StunModule.StunProxy = newproxy() --//Also, you should use a variable in the module instead of an attribute.

function bestunned()
	local CurrentProxy = newproxy() --//Declare new proxy.
	StunModule.StunProxy = CurrentProxy --//If this function is called again, the module's StunProxy variable will change.
	task.spawn(function()

		Humanoid:SetAttribute("Stunned", true)
		Humanoid.WalkSpeed = StunWalkSpeed
		Humanoid.JumpPower = StunJumpPower
		
		task.wait(Duration) --// Use task.wait() instead of wait.
		if Module.StunProxy == CurrentProxy then

			Humanoid:SetAttribute("Stunned", false)
			Humanoid.WalkSpeed = WalkSpeed
			Humanoid.JumpPower = JumpPower

		end
	end)
end

Oh, and compared to AkaNub’s code, this is closer to what you’d see in a fighting game where new stun takes priority over old stun. If someone stunned for 10s is hit at 5 seconds for 1 second of stun, they’ll recover in 1 second, not 5.

3 Likes

I had a similar problem with disabling movement/jumping, so I ended up using integer attributes instead of booleans to prevent the issue of the player being able to move when they shouldn’t.
I also used attributes for offsets and multipliers, to make sure nothing weird happens when increasing/decreasing walkspeed.

Your method seems interesting, however. It might be worthwhile for me to look into using newproxy() if I run into a similar issue for future projects.
It could also potentially work for an attribute system similar to Minecraft’s, where stat modifiers are given unique ids.

2 Likes

Ok I tried using this, but sometimes the player gets stun locked permanently except if you hit them again. How do you fix this and should the script be a local or server script?

What’s the code look like? The version I’ve written here shouldn’t have any logic errors.

1 Like

This is the module script I put into a folder in ReplicatedStorage:

local StunModule = {}

local CurrentProxy

local function SetDefaultHumanoidAttributes(Character : Instance)
	local Humanoid = Character:FindFirstChild("Humanoid")

	Humanoid:SetAttribute("WalkSpeed", Humanoid.WalkSpeed)
	Humanoid:SetAttribute("JumpPower", Humanoid.JumpPower)
end

function StunModule:Stun(Character : Model, Duration: number, StunWalkSpeed: number, StunJumpPower: number)
	local Humanoid = Character:FindFirstChild("Humanoid")

	if not Humanoid or Humanoid.Health <= 0 then return end
	
	local NewProxy = newproxy()
	CurrentProxy = NewProxy
	
	local WalkSpeed = Humanoid:GetAttribute("WalkSpeed")
	local JumpPower = Humanoid:GetAttribute("JumpPower")

	if not WalkSpeed or not JumpPower then
		SetDefaultHumanoidAttributes(Character)

		WalkSpeed = Humanoid:GetAttribute("WalkSpeed")
		JumpPower = Humanoid:GetAttribute("JumpPower")
	end
	
	task.spawn(function()

		Humanoid:SetAttribute("Stunned", true)

		Humanoid.AutoJumpEnabled = false
		Humanoid.WalkSpeed = StunWalkSpeed
		Humanoid.JumpPower = StunJumpPower

		task.wait(Duration)
		if CurrentProxy == NewProxy then

			Humanoid:SetAttribute("Stunned", false)

			Humanoid.AutoJumpEnabled = true
			Humanoid.WalkSpeed = WalkSpeed
			Humanoid.JumpPower = JumpPower

		end
		
	end)
end

return StunModule

I use a server script to call in the stun module function that works, but sometimes the one of the players are stunlocked until someone hits them again I dont know why.