Help with scripting a sword

The radius is 30 studs, it could be increased with the click of a key. I think you are being a bit extreme, the user would have to have extreme latency for it to take that long, in which case they are probably about to lose connection.

It would only be a matter of tweaking to avoid that, but I understand what you are saying.

With running it on the server though, the issue can be worse though imo because you can be clearly hitting a player on your client, especially if they are lagging, but on the server the hits aren’t firing because the player is just out of reach or moving to much from side to side.

Edit: let’s just settle with what you said a while ago, both have weaknesses. Whatever the OP wants, I’m willing to help him with.

Woah, a lot of stuff just happened. Remember, I am terrible at lua, so most of this stuff does not make sense to me. Therefore, whichever method works most efficiently, (in either of your opinions) is what I will try. How do I add that cooldown and range limit to my current script as well as be exploit-proof?
Here is the updated server script:

game.ReplicatedStorage.Damage.OnServerEvent:Connect(function(player, hit)
if hit.Parent:FindFirstChild("Humanoid")~= nil then
hit.Parent.Humanoid:TakeDamage(37)
game.StarterPack.Sword.Handle.Hit:Play()
end
end)

To be clear, is everything else working properly now? All you need to add is the server checks for anti-exploits (cooldown check and distance check)?

Edit: I did not get a notification, because you did not reply to me or mention me. Sorry for taking so long to see your post!

Oops, my bad. I meant to mention both of you but I guess I forgot. Yes, everything is working, all I need to do is add the anti-exploit code, which I don’t really know how to do.

To check the distance between two parts, use magnitude like so:

(part1.Position-part2.Position).magnitude -- this returns the distance in studs

To check how long it has been, you will create an Array that saves the tick() of their attack, and compare when they attack again. Here is what your server script should look like after these changes, note I also made some other updates as well, with notes to explain. (Also, I split up the if statement into multiple lines so it’s easier to read on here).

local lastAttack = {}
game.ReplicatedStorage.Damage.OnServerEvent:Connect(function(player, hit)
    if hit.Parent:FindFirstChild("Humanoid") ~= nil and player.Character ~= nil then
        if hit.Parent.Humanoid.Health > 0 and player.Character.Humanoid.Health > 0 then -- check if the users are alive, we don't want dead players dealing damage, or players dealing damage to dead players.
            if lastAttack[player.Name] == nil or tick() - lastAttack[player.Name] > 0.5 then
                if (hit.Parent.HumanoidRootPart.Position-player.Character.HumanoidRootPart.Position).magnitude > 40 then -- went with 40 just in case of serious latency. You can tweak this if wanted.
                    lastAttack[player.Name] = tick()
                    hit.Parent.Humanoid:TakeDamage(37)
                    local sword = player.Backpack:FindFirstChild'Sword' or player.Character:FindFirstChild'Sword' -- obtain the sword of the player attacking
                    if sword ~= nil then -- make sure it's not nil
                        sword.Handle.Hit:Play() -- You want to play the hit noise on the sword of the player who is attacking, not the sword in the StarterPack.
                    end
                end
            end
        end
    end
end)

Is there a need for the RemoteEvent? I feel like it’s kind of pointless if my local script can handle animations, and the server script handles the damage/hit aspects of the sword. I figured by using a bool value in the tool that I named CanDamage and then checking in the script if it is true to prevent idle damage, which is followed by everything else that happens when a player is hit by the sword (damage, SFX, etc.) I can have two completely separate scripts and I wouldn’t even need to go from client to server, etc. Unless animations being ran on a local script are only visible to that player, I don’t see a purpose. Also, would it be possible for exploiters to manipulate damage and can I protect that?

Nevermind, you would still need to trigger CanDamage somehow and that is done when the animation is told to play. I guess you do need a RemoteEvent.

1 Like

Yes, you definitely need a remote event. You are correct that you could use a remote event to change the bool value, and then run the hit code on the server, but this will not be perfect either since the players bool will have slight delay and may not be perfectly matched to their attack animation (ex. A player attacks, but the bool takes longer to register than the hit, and thus no damage is dealt).

So what was the alternative we decided on (in basic terms) that can run it without issues?

If this is not simple enough of an explanation, I will do my best to make if in fewer, simpler terms.

Thank you for the reminder. I have everything back to normal (I think) and the sword plays the animations but does not deal damage.
My current server script is the one you made above, with anti-lag and anti-exploit.
My current local script is this:
local CanAttack = true

local Attack_Animations = script.Parent:WaitForChild("Animations")

local Attack_Num = 1

local Anim_Name = "Attack"

local Audio = script.Parent.Handle.Swing

local trail = script.Parent.SwordBlade.Trail

script.Parent.Equipped:Connect(function()

local idle = script.Parent.Parent.Humanoid:LoadAnimation(script.Parent.Animations.Idle)

idle:Play()

script.Parent.Unequipped:Connect(function()

idle:Stop()

end)

end)

script.Parent.Activated:Connect(function()

game.ReplicatedStorage.Damage:FireServer()

local Animation = script.Parent.Parent.Humanoid:LoadAnimation(Attack_Animations[Anim_Name..Attack_Num])

if CanAttack == true then

Animation:Play()

Audio.PlaybackSpeed = math.random(90,110)/100

Audio:Play()

trail.Enabled = true

CanAttack = false

wait(0.65)

Animation:Stop()

Audio:Stop()

trail.Enabled = false

CanAttack = true

if Attack_Num == 1 then

Attack_Num = Attack_Num + 1

else if Attack_Num == 2 then

Attack_Num = 1

end

end

end

end)

It keeps pasting and formatting weird, sorry if something is off.

We made some changes earlier in this thread that you need to incorporate into your local script. If you are still not sure what needs to be done after reading the below code, don’t hesitate to ask!

Edit: is there a way to highlight text? It would be helpful for pointing out the specific lines to be noted.

Everything works, but it just doesn’t deal damage. The problem would have to be in the server script, I’m guessing. Also, if you select/highlight over text with your mouse you can quote certain areas.

Nevermind once again, I fixed it. In your code in the server script, you accidentally had the inequality sign as greater than instead of less than. So it would only deal damage if you were 40 studs or more away.
Edit: The sword still deals idle damage

1 Like

Haha! Good catch! Glad it is working for you now, I apologize for the typo.

My mistake, I should’ve tested before posting. The sword does idle damage, which can be fixed by adding a line in the local script.

blade.Touched:Connect(function(hit)
	if CanAttack == false then  --Add this.
		game.ReplicatedStorage.Damage:FireServer(hit)
	end
end)

That was one of the things I was referring to for you local script fixes. We covered that earlier in this article. Glad you were able to fix that on your own!

How could I add more weapons, now that I have that one working? The server script applies for every weapon in the game, and what if I wanted to change damage?

I would create a table on the server with each of the weapons names and damages. Then when the deal damage function is called on the server, deal the damage of the table[weapon.Name].

damageTable = {}
damageTable[weapon.Name] = 30
1 Like