Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
1

How to damage a player when they touch water?

Asked by 3 years ago

Here is what I have so far:

script:GetPropertyChangedSignal("Parent"):Connect(function()
    if script.Parent:IsA("Model") then
        local plr = game.Players:WaitForChild(script.Parent.Name)
        local rf = script.Parent:WaitForChild("RightFoot")
        local hum = rf.Parent:WaitForChild("Humanoid")
        local db = {}
        rf.Touched:Connect(function(hit)
            if hit.Material == Enum.Material.Water then
                if db[plr] == true then
                else
                    db[plr] = true
                    hum:TakeDamage(15)
                    wait(0.5)
                    db[plr] = nil
                end
            end
        end)
    end
end)

It is located in game > StarterPlayer > StarterCharacterScripts. It does not damage the player when they touch water and there are no errors, warnings, etc. Could someone point out what is wrong?

0
Can you show how you set up the scripts and where? XLeMysterioX 22 — 3y
0
This is the only script. PrismaticFruits 842 — 3y
0
Is the water terrain? Because if it is just a part, you could have the part not able to collide, and then just add a touched event to that JoRyJuKy 32 — 3y
0
Why do you need to detect when the Script's Parent object gets changed? And why do you need to detect if its parent is a Model??? Why are you retrieving the player by looking for a player with the same name as the character(localplayer exists)??? Water isn't a valid material for parts iirc, and you don't need a debounce array since this is for every individual player. brokenVectors 525 — 3y

3 answers

Log in to vote
0
Answered by
Amiaa16 3227 Moderation Voter Community Moderator
3 years ago

Scripts in StarterScripts are ran after they're parented to player/character, so your event will never fire, so simply remove the event part. Also you can use game.Players:GetPlayerFromCharacter(script.Parent) instead of what you're doing (I'm assuming it's a server script).

0
Did not work. I am just going to use a part instead. PrismaticFruits 842 — 3y
Ad
Log in to vote
0
Answered by 3 years ago

There's two ways you can do this. You can use

Region = Region3.new()

or you could check if the characters state is swimming

if Humanoid:GetState() == Enum.HumanoidStateType.Swimming then 
   --Do damage
end

Granted, a region3 would work the best, as the character isnt always swimming if they touch the slightest amount of water.

0
I have absolutely no clue what Region3 is. PrismaticFruits 842 — 3y
0
https://developer.roblox.com/en-us/api-reference/datatype/Region3 It's just like an invisible area that is created, and whenever something is inside that area, you can make it do something. So in this example, you can check if a player is in the region, and if they are you deal damage to them DarkDanny04 407 — 3y
Log in to vote
0
Answered by 3 years ago
Edited 3 years ago

I found the best method for getting players server side (as attempting to damage a humanoid on local side is wrong), is to connect an event to both when the player joins, and they get a character. After that, a constant check is operated 1-5 times a second, checking to see if they are in water.

For the check itself, as mentioned by DarkDanny04, are the region and the humanoid state. To quantify this, I decided to have a longer check whilst not in water, then once in water, the check is decreased so that there is not as much resources used until the player is at the water (you could, just make the loop without a second loop inside, for easier coding). For the region method, you define the region and expand it to the grid that the terrain uses (size of 4x4x4) and then check the voxel. For more information on exactly how reading voxels works, I'd suggest reading this: Terrain:ReadVoxels If it's a water material, it will continue to damage them until the check says it is not. Here's the code for the region checking:

local debounce = false

game.Players.PlayerAdded:Connect(function(plr)
    plr.CharacterAdded:Connect(function()
        local Char = plr.Character
        print("character made")
        local Humanoid = Char:WaitForChild("Humanoid")
        while Humanoid.Health > 0 do
            wait(0.2)
            local State = Humanoid:GetState()

            local head = plr.Character:WaitForChild("Head")
            local root = plr.Character:WaitForChild("HumanoidRootPart")

            local humRegion = Region3.new(Vector3.new(root.Position.X - 0.5 , root.Position.Y - 1, root.Position.Z - .5), Vector3.new(root.Position.X +.5  , root.Position.Y +1, root.Position.Z +.5) ):ExpandToGrid(4)
            --print(humRegion.CFrame, root.CFrame)
            --print(humRegion.Size)


            local material = workspace.Terrain:ReadVoxels(humRegion,4)[1][1][1]

            if material == Enum.Material.Water and plr.Character.Humanoid.FloorMaterial == Enum.Material.Air and debounce == false then
                debounce = true
                local underwater = true
                while underwater == true do
                    print("Underwater")
                    Char.Humanoid:TakeDamage(5) 
                    humRegion = Region3.new(Vector3.new(root.Position.X - 0.5 , root.Position.Y - 1, root.Position.Z - .5), Vector3.new(root.Position.X +.5  , root.Position.Y +1, root.Position.Z +.5) ):ExpandToGrid(4)

                    material = workspace.Terrain:ReadVoxels(humRegion,4)[1][1][1]
                    if material ~= Enum.Material.Water or plr.Character.Humanoid.FloorMaterial ~= Enum.Material.Air then
                        underwater = false
                        print("Not underwater")
                    end
                    wait(0.1)
                end
                debounce = false
            else
                wait(0.1)
            end
        end 
    end)

end)

(Please be aware I'm not full adept in terrain and voxels, so that method's code is a little all over the place)

The other method, humanoid:getstate() is a lot more simplistic, but doesn't return positive if the player is standing on land in water. You may want this if you have shallow water on your map as well as the deeper oceans (To get around the problem of stand on the water you want to damage, make it deeper).

game.Players.PlayerAdded:Connect(function(plr)
    plr.CharacterAdded:Connect(function()
        local Char = plr.Character
        print("character made")
        local Humanoid = Char:WaitForChild("Humanoid")
        while Humanoid.Health > 0 do
            wait(0.2)
            local State = Humanoid:GetState()

            if State == Enum.HumanoidStateType.Swimming then
                print("Started swimming")
                while State == Enum.HumanoidStateType.Swimming do
                    print("Swimming")
                    State = Humanoid:GetState()
                    Humanoid:TakeDamage(5)
                    wait(0.1)
                end
                print("No longer swimming")
            end
        end
    end)

end)

Personally, I'd use the humanoid state for flexibility on shallow water, and simpler code, But it's up to you. Here's an uncopylocked game where you can see it all and just copy either scripts.

Hope this has been helpful.

0
I am busy right now so I will try it out in a week or so. PrismaticFruits 842 — 3y

Answer this question