Knife that Only Is Given At Nighttime

Goal:

  • During nighttime, a knife model from Lighting will be cloned to the player’s backpack.
  • Once night is over, the player’s inventory will clear. (I would prefer it to be the knife is removed but I don’t know how to do that. So if anybody does, that would be cool.)

My script:

local Lighting = game:GetService("Lighting")  -- get lighting

game.Players.PlayerAdded:connect(function(player)
	
	Lighting:GetPropertyChangedSignal("ClockTime"):Connect(function()
		
			if Lighting.ClockTime <= 5 or Lighting.ClockTime >= 20 then -- if night
				local knife = game.Lighting.Knife:Clone()
				knife.Parent = player.Backpack -- clone and move to player backpack
				print("Knife was given")
			end

			if Lighting.ClockTime >= 5 or Lighting.ClolckTime <= 20 then
				player.Backpack:ClearAllChildren()
				print("Backpack cleared")
			end
		end
	end)
end)

My problem:

The code works, but it spams knifes and will not stop. I tried fixing this by adding wait(540) … that is how long nighttime is… at the end of the code but it did not work.

2 Likes

You can add an if statement to check if the player already has the knife.

1 Like

I think this is what you need:

local Lighting = game:GetService("Lighting")  -- get lighting

game.Players.PlayerAdded:connect(function(player)

	Lighting:GetPropertyChangedSignal("ClockTime"):Connect(function()

		if Lighting.ClockTime <= 5 or Lighting.ClockTime >= 20 then -- if night
			if not player.Backpack:FindFirstChild("Knife") then
				local knife = game.Lighting.Knife:Clone()
				knife.Parent = player.Backpack -- clone and move to player backpack
				print("Knife was given")
			else
				warn("Player already has knife.")
			end
		end

		if Lighting.ClockTime >= 5 or Lighting.ClockTime <= 20 then
			player.Backpack:ClearAllChildren()
			print("Backpack cleared")
		end
	end)
end)
2 Likes

You’ll also want to account for the case where a player has the knife equipped right before the nighttime period ends, if I’m not mistaken. A tool doesn’t only exist within the backpack in an active game. When it’s equipped, it’ll be moved to the character.

1 Like

Thank you so much for the response! Although, I tested out the script, and the knife does not appear; it just spams the print in the output bar. Any ideas?

You mean this?

		if Lighting.ClockTime <= 5 or Lighting.ClockTime >= 20 then -- if night
			if not player.Backpack:FindFirstChild("Knife") then
				if not player.Character:FindFirstChild("Knife") then
					local knife = game.Lighting.Knife:Clone()
					knife.Parent = player.Backpack -- clone and move to player backpack
					print("Knife was given")
				end
			else
				warn("Player already has knife.")
			end
		end

I think that part’s fine. It’s right here that I’m referring to:

if Lighting.ClockTime >= 5 or Lighting.ClockTime <= 20 then
	player.Backpack:ClearAllChildren()
	print("Backpack cleared")
end

This code is removing the knife from the backpack once the night is over, right?

But what if, just right before the night is over, the player still has the knife equipped so that its in the character?

That means player.Backpack:ClearAllChildren() won’t do much because it’d be clearing no tools.
To fix it, you’d want to see if the knife exists inside the character and destroy it there, then check the backpack if it doesn’t exist in the character.

Also, ClearAllChildren works great but what if the OP decides to add more tools in the future? You might want to edit it so that the solution is future-proof for more tools. (Destroy the knife specifically, nothing else).

There’s also another major problem with the current code! A potential memory leak. Basically just remove the nested connections (remove PlayerAdded from all of it)

2 Likes

I know I’m responding with a lot of feedback to the guy helping the OP, but it’s just to help!
I might rewrite their code from scratch.

So,

if player.Backpack:FindFirstChild("Knife") then
    player.Backpack:FindFirstChild("Knife"):Destroy()
elseif player.Character:FindFirstChild("Knife") then
    player.Character:FindFirstChild("Knife"):Destroy()
end
1 Like

would be more of:

		if Lighting.ClockTime <= 5 or Lighting.ClockTime <= 20 then
				if player.Backpack:FindFirstChild("Knife") then
				player.Backpack:FindFirstChild("Knife"):Destroy()
				print("Knife cleared")
			elseif player.Character:FindFirstChild("Knife") then
				player.Character:FindFirstChild("Knife"):Destroy()
				print("Knife cleared")
			end
		end
2 Likes
local knifeCheck = player.Backpack:FindFirstChild("Knife") or player.Character:FindFirstChild("Knife")

if knifeCheck then
	knifeCheck:Destroy()
end
2 Likes

By the way, why put the knife in lighting instead of serverstorage?
edit: oh wait thats in the original script

The code should be:

-- LocalScript
local replicatedStorage = game:GetService("ReplicatedStorage")
local giveKnifeEvent = replicatedStorage.GiveKnifeEvent
local removeKnifeEvent = replicatedStorage.RemoveKnifeEvent

local lighting = game:GetService("Lighting")
lighting:GetPropertyChangedSignal("ClockTime"):Connect(function()
    local currentClockTime = lighting.ClockTIme
    if (currentClockTime <= 5 or currentClockTime >= 20) then
        giveKnifeEvent:FireServer()
    elseif (currentClockTime >= 5 or currentClockTIme <= 20) then
        removeKnifeEvent:FireServer()
    end
end)

^^^I haven’t worked with ClockTime as much so I only used the same checks as you did.

-- Script
local replicatedStorage = game:GetService("ReplicatedStorage")
local giveKnifeEvent = replicatedStorage.GiveKnifeEvent
local removeKnifeEvent = replicatedStorage.RemoveKnifeEvent

local knifeToolName = "Knife"
local knife = -- Refer to your knife here

giveKnifeEvent.OnServerEvent:Connect(function(player)
    local character = player.Character
    if (character) then
        local backpack = player.Backpack
        local isKnifeInChar = character:FindFirstChild(knifeToolName)
        local isKnifeInBackpack = backpack:FindFirstChild(knifeToolName)
        if (isKnifeInChar or isKnifeInBackpack) then
            return
        end
        local knifeClone = knife:Clone()
        knifeClone.Parent = backpack
    end
end)

removeKnifeEvent.OnServerEvent:Connect(function(player)
    local character = player.Character
    if (character) then
        local backpack = player.Backpack
        local knifeInChar = character:FindFirstChild(knifeToolName)
        local knifeInBackpack = backpack:FindFirstChild(knifeToolName)

        if (knifeInChar) then
            knifeInChar:Destroy()
        elseif (knifeInBackpack) then
            knifeInBackpack:Destroy()
        end
    end
end)

It’s a lot more than the original but this is the way it had to be done to respect FilteringEnabled, if I’m not wrong.

@triisflame You’ll need to create two remote events and refactor your code based on this.
If I’m not crazy, this should work fine if you port it properly. (Don’t copy and paste, try to rewrite it in your codebase).

1 Like

Unless the time is changing on the client only (unlikely the case) why not just loop through all the players if the new time is <= 5, >= 20, >= 5 or <= 20?

That is another way you can go about it :smiley:! Personally I’d still stick with using remotes and their events because it’ll help keep the client side cleaner and more separated from the server side.

Imagine if the OP wants something to happen locally to the player after the knife is given.
With your suggestion, you’d have to rely on extra functionality like FireClient/FireAllClients on the server side instead of just being able to write to the LocalScript directly.

But it really doesn’t matter, feel free to write an alternative solution using a loop on the server side.

I understand that but you also shouldn’t use unnecessary remotes. It seems like the only functionality of this script is to give or remove player’s knives whenever it is a certain time of day.

Two remotes sending no arguments except the player who fired wouldn’t really have a major effect! I wouldn’t worry about choosing my solution against your suggestion because a lot of Roblox games use many remotes anyway while still maintaining their performance. It’s not unnecessary because this is what the client-server model requires for Roblox games to function!
I think it’s a matter of quality over quantity.

It doesn’t make sense using PlayerAdded and Light change in one script smh.
I am guessing this is what you are looking for?

local Lighting = game:GetService("Lighting")

local There  = false

local function GiveKnife(Player: Player)
        local knife = Lighting.Knife:Clone()
        local Backpack = Player:FindFirstChild("Backpack")
	knife.Parent = Backpack
	print("Knife was given")
end

local function RemoveKnife(Player: Player)
        local Character = Player.Character or Player
        local Backpack = Player:FindFirstChild("Backpack") or Player
        local Knife = Character:FindFirstChild("Knife") or Backpack:FindFirstChild("Knife")
	if Knife then
            Knife:Destroy()
        end
	print("Knife was Removed ")
end
	Lighting:GetPropertyChangedSignal("ClockTime"):Connect(function()
		
			if Lighting.ClockTime <= 5 or Lighting.ClockTime >= 20 and not There then -- if night
				for i, v in pairs(game:GetService("Players"):GetPlayers()) do			    
				GiveKnife(v)
			        end
                              There = true
                        end

			if Lighting.ClockTime >= 5 or Lighting.ClolckTime <= 20 and There then
			      for i, v in pairs(game:GetService("Players"):GetPlayers()) do			    
				RemoveKnife(v)
			        end
                        There = false
			end
		end
	end)
end)

sorry for bad formating.

Something like this should be what you’re looking for, although @magical_artistery’s solution would work just as well. Depends on which approach you would rather take. :slight_smile:

local players = game:GetService("Players")
local lighting = game:GetService("Lighting")

local knife = lighting.Knife

local function giveKnives()
	for _, player in players:GetPlayers() do
		if not player.Backpack:FindFirstChild("Knife") and not player.Character:FindFirstChild("Knife") then
			local newKnife = knife:Clone()
			newKnife.Parent = player.Backpack
		end
	end
end

local function removeKnives()
	for _, player in players:GetPlayers() do
		local knifeCheck = player.Backpack:FindFirstChild("Knife") or player.Character:FindFirstChild("Knife")
		
		if knifeCheck then
			knifeCheck:Destroy()
		end
	end
end

lighting:GetPropertyChangedSignal("ClockTime"):Connect(function()
	local clockTime = lighting.ClockTime
	
	if clockTime <= 5 or clockTime >= 20 then
		giveKnives()
	elseif clockTime >= 5 or clockTime <= 20 then
		removeKnives()
	end
end)
2 Likes

Thank you! Do you mind if I ask if there are any major pros with using remote events for this instead of functions?