So, I have this coroutine that regenerates health based on a value inside the player’s “stats” folder. However, whenever this executes, I found that the player’s health is only regenerated by the “Health” script in the Character model. I created a “Health” script in StarterPlayerScripts, disabling the default Health script. This fixed the issue, and my coroutine ran fine.
So, my question is, how can I enable the Health script (or use a custom one) to have both a regeneration script and my coroutine? I realize that this may sound like a weird ask, but the idea is that the user’s health regenerates over time, but when they use an ability, it restores some amount of health to the player over some duration in ticks. i.e. The player’s health is at 50/100. Standard regeneration restores some amount every second. The player uses an ability and heals 10 health per second for five seconds ON TOP of the standard regeneration.
Coroutine:
for _, v in pairs(workspace:GetDescendants()) do
if v.ClassName == "Highlight" and v.Parent == player.Character then
-- heal function
coroutine.wrap(function()
-- heal some amount of health over some duration of time
for i = 1, tool.Stats.Duration.Value, 1 do
v.Parent.Humanoid.Health += heal
task.wait(1)
end
end)()
debounce = true
break
else
debounce = false
end
end
Custom Health script:
--------------------------------------------------------------------------------
local Character = script.Parent
local Humanoid = Character:WaitForChild'Humanoid'
local player = nil
for _, v in game:GetService("Players"):GetChildren() do
if v.Name == Character.Name then
player = v
end
end
local REGEN_RATE = player:WaitForChild("stats"):FindFirstChild("regen").Value -- Regenerate this fraction of MaxHealth per second.
local REGEN_STEP = 3 -- Wait this long between each regeneration step.
--------------------------------------------------------------------------------
while true do
while Humanoid.Health < Humanoid.MaxHealth do
local dt = wait(REGEN_STEP)
print("REGEN: ", REGEN_RATE)
print("Before: ", Humanoid.Health)
Humanoid.Health += REGEN_RATE
print("After: ", Humanoid.Health)
end
Humanoid.HealthChanged:Wait()
end
Here is the Roblox default health regen for reference:
-- Gradually regenerates the Humanoid's Health over time.
local REGEN_RATE = 1/100 -- Regenerate this fraction of MaxHealth per second.
local REGEN_STEP = 1 -- Wait this long between each regeneration step.
--------------------------------------------------------------------------------
local Character = script.Parent
local Humanoid = Character:WaitForChild'Humanoid'
--------------------------------------------------------------------------------
while true do
while Humanoid.Health < Humanoid.MaxHealth do
local dt = wait(REGEN_STEP)
local dh = dt*REGEN_RATE*Humanoid.MaxHealth
Humanoid.Health = math.min(Humanoid.Health + dh, Humanoid.MaxHealth)
end
Humanoid.HealthChanged:Wait()
end
I think you mean to multiply REGEN_RATE by dt, otherwise the units don’t make sense. You’re basically saying that a heal rate of “5% per second” means the health will go from 50/100 to 50.05/100 after one second.
That’s not really my problem. Both my script and the custom script work fine independently of each other. The problem is that the custom Health script overwrites the changes to Humanoid.Health from my regeneration script. I think the reason is because I’m writing to Humanoid.Health at the same time from two different scripts.
Let’s say that Humanoid.Health is accessed from the Health script when it is at 50/100. It adds some amount to the health.
At the same time, I activated my regeneration script, which adds some amount of health on top of that. Health at this point was also 50/100. Well, when this script adds health to Humanoid.Health, that change gets reversed by the Health script above. Instead of adding 20 health in the regen script, I take an amount of damage equivalent to the health restored by the regen script and then add 10 health from the Health script.
This renders my regeneration script effectively useless. The only time it works is when I disable the Health script entirely. But I want both.
I tested this and couldn’t figure out why it wouldn’t stack the way you want. You could try printing the before and after health values in each script to see if it is indeed a concurrent issue.
When I print, I see that the regen script changes, then the screen flashes red as I take damage and the Health script kicks in. Here’s the results. Keep in mind that the Regen script occurs every 1 second, whereas my custom Health script runs once ever 3 seconds. Could it be that the changes aren’t being synced across the client and server?
10:49:07.563 Regen Script - Health Before: 822 - Client - Script:54
10:49:07.563 Regen Script - Health After: 920 - Client - Script:56
10:49:08.226 Health Script - Health Before: 822 - Server - Health:22
10:49:08.226 Health Script - Health After: 850 - Server - Health:24
10:49:08.578 Regen Script - Health Before: 850 - Client - Script:54
10:49:08.578 Regen Script - Health After: 920 - Client - Script:56
10:49:09.594 Regen Script - Health Before: 920 - Client - Script:54
10:49:09.594 Regen Script - Health After: 920 - Client - Script:56
10:49:10.595 Regen Script - Health Before: 920 - Client - Script:54
10:49:10.595 Regen Script - Health After: 920 - Client - Script:56
10:49:11.243 Health Script - Health Before: 850 - Server - Health:22
10:49:11.243 Health Script - Health After: 878 - Server - Health:24
10:49:11.595 Regen Script - Health Before: 878 - Client - Script:54
10:49:11.595 Regen Script - Health After: 920 - Client - Script:56
10:49:14.243 Health Script - Health Before: 878 - Server - Health:22
10:49:14.243 Health Script - Health After: 906 - Server - Health:24
Its possible that if Health is being set from the client and from the server at the same time that it wont check which one happened first and could cause your problem. I’m not sure of a good way to check this.
Personally I would take an approach by modifying the default “Health” script to respect some boosted regen stat, and then directly modify this boosted regen stat. In this example I have set BoostedRegen to 0.1 so they will regenerate (0.01 + 0.1)% Health every second.
Example Script (Named Health and in StarterCharacterScripts to override the default)
local Character = script.Parent
local Humanoid = Character:WaitForChild("Humanoid")
local REGEN_RATE = 1
local REGEN_PRECENTAGE = 1/100
while true do
while Humanoid.Health < Humanoid.MaxHealth do
task.wait(REGEN_RATE)
local BoostedRegen = Humanoid:GetAttribute("BoostedRegen")
if BoostedRegen and BoostedRegen ~= 0 then
Humanoid.Health = math.min(Humanoid.Health + Humanoid.MaxHealth * (REGEN_PRECENTAGE + BoostedRegen), Humanoid.MaxHealth)
else
Humanoid.Health = math.min(Humanoid.Health + Humanoid.MaxHealth * REGEN_PRECENTAGE, Humanoid.MaxHealth)
end
end
Humanoid.HealthChanged:Wait()
end
I resolved the issue. As I suspected before, the issue was that the changes to the health were made on the client side in one script and on the server side in the other. I resolved this issue by creating a RemoteEvent and firing it from the regeneration local script to the server to update the health. No update needed to be made to the custom Health script.
LocalScript:
for _, v in pairs(workspace:GetDescendants()) do
if v.ClassName == "Highlight" and v.Parent == player.Character then
local duration = tool.Stats.Duration.Value
coroutine.wrap(function()
HealedEvent:FireServer(tool, heal, duration)
end)()
debounce = true
break
else
debounce = false
end
end
SeverScript:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local HealedEvent = ReplicatedStorage:WaitForChild("Healed")
local function regen(player, tool, heal, duration)
for i = 1, duration, 1 do
print("Regen Script - Health Before: ", player.Character.Humanoid.Health)
player.Character.Humanoid.Health += heal
print("Regen Script - Health After: ", player.Character.Humanoid.Health)
task.wait(1)
end
end
HealedEvent.OnServerEvent:Connect(regen)
Hi, just letting you know this is a terrible idea! You are letting the client have control over the player’s health - meaning an exploiter could simply fire the remote and regenerate their health as they wish.
I suggest to either add some sort of sanity check or, better, remake your system from scratch by making it completely server-sided and leaving no access to the client.
Thanks for the heads-up. I don’t know a lot about secure scripting, so that just made my job a lot more difficult, lol. But it needs to be done! There are a few places where I’m firing remote events from the client to the server to change some stat (damage to enemies, stamina cost, health regen, etc). I’ll have to look deep into my code to figure out how I can move all of this server-side. Especially considering some of these things are stored on the localPlayer - just to make things more difficult.
But really, I’m grateful for your input. I’ll look into this. Thanks!