Creating easy streak items

I’m wanting to have multiple streak items, and here’s a quick example for grenades. I fear this is incredibly cluttered and poorly written.

Basically, I require all the modules on the server, and then the client requires and fires the .Use() function when they use the item.

This is currently flawed as well, as I create the grenade on the client (so theres a quick response between throwing the grenade/etc), and I also create the grenade on the server, and then delete the server grenade from our own client (so you don’t see 2 grenades.)

Is there better ways of handling this? What can I do different/better. Ideally, the main thing that needs to stay is Grenade.Use() as thats the function the client fires when they use said streak item. And then everything else in the code is run on start of server.

local Grenade = {}

local CollectionService = game:GetService("CollectionService")
local Debris = game:GetService("Debris")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")

local GrenadeItem = script:WaitForChild("Grenade")

local GameSettings = ReplicatedStorage:WaitForChild("GameSettings")
local Shared = ReplicatedStorage:WaitForChild("Shared")

local Maid = require(Shared.Maid)

local Camera = workspace.CurrentCamera

local SPEED = 3
local EXPLODE_TIME = 2
local DIAMETER = 30

local Explode = script:WaitForChild("Explode")
local Throw = script:WaitForChild("Throw")

--// Create the grenade
local function CreateGrenade(player, cFrame, client)
	local GrenadeItem = GrenadeItem:Clone()
	GrenadeItem.BrickColor = player.TeamColor
	
	GrenadeItem:SetAttribute("Creator", player.Name)
	GrenadeItem:SetAttribute("Client", client)
	
	CollectionService:AddTag(GrenadeItem, "Grenade")
	
	GrenadeItem.Parent = workspace
	
	GrenadeItem.CFrame = cFrame * CFrame.new(0, -0.5, -2)
	GrenadeItem:ApplyImpulse(GrenadeItem.CFrame.LookVector * SPEED)
	
	return GrenadeItem
end

--// Use the grenade streak
function Grenade.Use()
	if RunService:IsClient() then
		local Player = Players.LocalPlayer
		
		local GrenadeItem = CreateGrenade(Player, Camera.CFrame, true)
		
		Throw:FireServer(Camera.CFrame) -- Throw on the server
		
		delay(EXPLODE_TIME, function()
			GrenadeItem.Anchored = true
			GrenadeItem.Transparency = 1
			
			local Explode = GrenadeItem:Clone()
			Explode.Transparency = 0
			
			Explode.Parent = GrenadeItem
			
			local ExplodeTween = TweenService:Create(
				Explode,
				TweenInfo.new(0.5),
				{
					Size = Vector3.new(DIAMETER, DIAMETER, DIAMETER),
					Transparency = 1
				}
			)
			ExplodeTween:Play()
			ExplodeTween.Completed:Wait()
			
			GrenadeItem:Destroy()
		end)
	end
end

if RunService:IsServer() then	
	--// Character was hit
	local function CharacterHit(player, hitPlayer, hitCharacter)		
		if not hitPlayer or not hitCharacter then return end
		
		if hitPlayer.Character ~= hitCharacter then return end
		
		if player == hitPlayer then return end -- Creator can't tag themselves	
		
		local Teams = GameSettings:GetAttribute("Teams")
		if player.Team == hitPlayer.Team and Teams then return end -- Can't tag your own team
		
		local Humanoid = hitCharacter:FindFirstChildWhichIsA("Humanoid")
		if not Humanoid then return end
		
		-- Create all values associated with Creator
		local CreatorValue = Instance.new("ObjectValue")
		CreatorValue.Name = "Creator"
		CreatorValue.Value = player
		
		-- Player's level
		local LevelValue = Instance.new("NumberValue")
		LevelValue.Name = "Level"
		--LevelValue.Value = player.PlayerData.Level.Value
		LevelValue.Parent = CreatorValue
		
		-- Grenade used
		local EquippedValue = Instance.new("StringValue")
		EquippedValue.Name = "Equipped"
		EquippedValue.Value = "Grenade"
		EquippedValue.Parent = CreatorValue
		
		CreatorValue.Parent = Humanoid
		
		Humanoid:TakeDamage(100) -- Kill player
	end
	
	--// Grenade has been thrown
	local function GrenadeThrown(throwingPlayer, cFrame)
		local GrenadeItem = CreateGrenade(throwingPlayer, cFrame)
		
		delay(EXPLODE_TIME, function()
			GrenadeItem.Anchored = true
			GrenadeItem.Transparency = 1
			
			local Explode = GrenadeItem:Clone()
			Explode.Transparency = 0
			
			Explode.Parent = GrenadeItem
			
			local ExplodeTween = TweenService:Create(
				Explode,
				TweenInfo.new(0.5),
				{
					Size = Vector3.new(DIAMETER, DIAMETER, DIAMETER),
					Transparency = 1
				}
			)
			ExplodeTween:Play()
			
			coroutine.wrap(function()
				ExplodeTween.Completed:Wait()
				
				GrenadeItem:Destroy()
			end)()
			
			local Teams = GameSettings:GetAttribute("Teams")
			
			-- Check radius of players
			for _, player in pairs(Players:GetPlayers()) do
				if player == throwingPlayer then continue end -- Don't affect ourselves
				
				if player.Team == throwingPlayer.Team and Teams then continue end -- Don't affect team mates
				
				local Character = player.Character
				if not Character then continue end
				
				if (Character.HumanoidRootPart.Position - GrenadeItem.Position).Magnitude <= DIAMETER / 2 then	
					CharacterHit(throwingPlayer, player, Character)
				end
			end
		end)
	end
	
	Throw.OnServerEvent:Connect(GrenadeThrown)
elseif RunService:IsClient() then
	local Player = Players.LocalPlayer
	
	--// Remove the server grenade from our client (so no double grenade visual)
	local function RemoveGrenade(grenade)
		if grenade:GetAttribute("Client") then return end
		
		if grenade:GetAttribute("Creator") ~= Player.Name then return end -- Not our grenade
		
		RunService.RenderStepped:Wait()
		
		grenade:Destroy()
	end
	
	CollectionService:GetInstanceAddedSignal("Grenade"):Connect(RemoveGrenade)
end

return Grenade

NOTE I don’t want to split this up into seperate modules. All streak items will have their own module, and each module would handle that specific streak items information (for example sentries, radars, landmines, etc.)

1 Like