How can I update the hit position every bullet?

So this morning i got the nice idea to port my shooting into something more OOP oriented (atleast that’s what i think i’m doing)

it’s been going smoothly, buuut automatics kind of, shoot once then fire the updated hit pos remote but the gun itself doesn’t shoot

and i’m gonna be honest i entirely forgot how i fixed this in the past, so instead of brainstorming this i’d rather just ask on here

do note that i think some of this code suffers from unnecessary repetition so i apologize if it hurts your eyes (it’s the thought that counts right?)


client-sided functions (they get called and parameters are passed and that’s it

-- the "ApplyRecoil" function is just static recoil that i don't feel the need to include as it doesn't cause any issues

local shootFunctions = {
	SEMI = function(weapon: Tool, mouse: Mouse)
		local FIRERATE = gunStats[weapon.Name]["FIRERATE"]
		
		shootEvent:FireServer(mouse.Hit.Position)
		ApplyRecoil(weapon)
		task.wait(FIRERATE)
	end,
	
	AUTOMATIC = function(weapon: Tool, mouse: Mouse, shootAnim: Animation) -- It happens here!!
		local FIRERATE = gunStats[weapon.Name]["FIRERATE"]
		shooting = true
		shootEvent:FireServer(mouse.Hit.Position)
		
		local buttonUpConnection
		buttonUpConnection = mouse.Button1Up:Connect(function()
			disableIsHoldingEvent:FireServer()
			shooting = false
			buttonUpConnection:Disconnect()
		end)
		
		while shooting do
			if weapon.Bullets.Value < 1 then
				disableIsHoldingEvent:FireServer()
				shooting = false
				break
			end
			
			updateHitPosEvent:FireServer(mouse.Hit.Position)
			shootAnim:Play()
			ApplyRecoil(weapon)
			task.wait(FIRERATE)
		end
	end,
	
	SHOTGUN = function(weapon: Tool, mouse: Mouse, shootAnim: Animation)
		local FIRERATE = gunStats[weapon.Name]["FIRERATE"]
		
		shootEvent:FireServer(mouse.Hit.Position)
		shootAnim:Play()
		ApplyRecoil(weapon)
		task.wait(FIRERATE)
	end,
	
	AUTO_SHOTGUN = function(weapon: Tool, mouse: Mouse, shootAnim: Animation) -- This is unused for now.
		local FIRERATE = gunStats[weapon.Name]["FIRERATE"]
		shooting = true
		
		local buttonUpConnection
		buttonUpConnection = mouse.Button1Up:Connect(function()
			disableIsHoldingEvent:FireServer()
			shooting = false
			buttonUpConnection:Disconnect()
		end)
		
		while shooting do
			if weapon.Bullets.Value < 1 then
				disableIsHoldingEvent:FireServer()
				shooting = false
				break
			end
			
			updateHitPosEvent:FireServer(mouse.Hit.Position)
			shootAnim:Play()
			ApplyRecoil(weapon)
			task.wait(FIRERATE)
		end
	end,
}

server-sided script that receives the remote events (this is NOT the module script)

ShootEvent.OnServerEvent:Connect(function(player, initialHitPos)
	local tool = player.Character:FindFirstChildOfClass("Tool")	

	local bullets = tool.Bullets	
	local spread = tool.Spread

	local FIRERATE = gunInfo[tool.Name]["FIRERATE"]

	if tool.Shooting.Value then player:Kick("You need more power.") return end
	if tool.Reloading.Value then player:Kick("Why?") return end
	if tool.Equipping.Value then player:Kick("Someone's in a hurry.") return end
	if bullets.Value < 1 then player:Kick("Your manipulation techniques are atrocious.") return end
	if bullets.Value > gunInfo[tool.Name]["MAX_BULLETS"] then player:Kick("The underworld calls to you.") return end

	local hitPos = initialHitPos

	local updateHitPosConnection = UpdateHitPosEvent.OnServerEvent:Connect(function(updatePlayer, newHitPos)
		if updatePlayer == player then
			hitPos = newHitPos
		end
	end)

	coroutine.wrap(function() -- is this unnecessary? probably
		tool.FlashPart.FlashImage.Enabled = true
		tool.FlashPart.Light.Enabled = true
		task.wait(0.1)
		tool.FlashPart.FlashImage.Enabled = false
		tool.FlashPart.Light.Enabled = false
	end)()

	shootingModule.Shoot(tool.FlashPart, player, hitPos, tool)
	tool.Shooting.Value = true
	tool.Bullets.Value -= 1
	tool.FlashPart.ShootSound:Play()

	task.wait(FIRERATE)
	tool.Shooting.Value = false
	updateHitPosConnection:Disconnect()
end)

server-sided module script that does all the raycasting (this shouldn’t cause any issues but feel free to investigate it, just note that it’s nearly 200 lines of code)

local rStorage = game:GetService("ReplicatedStorage")
local dService = game:GetService("Debris")
local sScriptService = game:GetService("ServerScriptService")
local players = game:GetService("Players")

-- Events;
local playerEvents = rStorage:WaitForChild("PlayerEvents")
local damageIndicatorEvent = playerEvents.DamageGUI

-- Third party modules;
local weapons = sScriptService:WaitForChild("Weapons")
local guns = weapons:WaitForChild("Gun")
local gunInfo = require(guns.GunStats)




-- // NORMAL RAYCASTING FUNCTION
local function Raycasting(origin: Part, player: Player, hitPos: Mouse, weapon: Tool)
	local HEADSHOT_DAMAGE = gunInfo[weapon.Name]["HEADSHOT_DAMAGE"]
	local BODYSHOT_DAMAGE = gunInfo[weapon.Name]["BODYSHOT_DAMAGE"]

	-- Raycast parameters. 	
	local raycastParams = RaycastParams.new()
	raycastParams.FilterType = Enum.RaycastFilterType.Exclude
	raycastParams.FilterDescendantsInstances = {player.Character}
	raycastParams.IgnoreWater = true

	local direction = (hitPos - origin.Position).Unit
	local displacement = direction * gunInfo[weapon.Name]["MAX_DISTANCE"]
	local result = workspace:Raycast(
		origin.Position,
		displacement,
		raycastParams
	)

	local endPos = nil


	-- Did the raycast hit something?
	if result then
		endPos = result.Position
		local character = result.Instance.Parent
		local humanoid = character:FindFirstChild("Humanoid")

		if humanoid ~= players:GetPlayerFromCharacter(character) then 
			if result.Instance == character.Head then
				humanoid:TakeDamage(HEADSHOT_DAMAGE)
				damageIndicatorEvent:FireClient(player, HEADSHOT_DAMAGE, humanoid)
			else
				humanoid:TakeDamage(BODYSHOT_DAMAGE)
				damageIndicatorEvent:FireClient(player, BODYSHOT_DAMAGE, humanoid)
			end
		end
	else
		endPos = origin.Position + displacement
	end

	local tracer = Instance.new("Part")
	tracer.Parent = workspace
	tracer.Anchored = true
	tracer.CanCollide = false
	tracer.CanQuery = false
	tracer.Transparency = 0.3
	tracer.Color = Color3.new(0.960784, 0.666667, 0.156863)
	dService:AddItem(tracer, 0.2)

	local tracerLength = (endPos - origin.Position).Magnitude
	tracer.Size = Vector3.new(0.07, 0.07, tracerLength)
	tracer.CFrame = CFrame.lookAt(origin.Position + (endPos - origin.Position) / 2, endPos)
	
	if weapon.AmmoType.Value == "NRG" then
		tracer.Color = Color3.fromRGB(0,255,0)
	end
end

------------------------------------------------------------------------

-- // SHOTGUN RAYCASTING FUNCTION
local function ShotgunRaycasting(origin: Part, player: Player, hitPos: Mouse, weapon: Tool)
	local PELLETS = gunInfo[weapon.Name]["PELLETS"]
	local HORIZONTAL_SPREAD = {min = -0.2, max = 0.4}
	local VERTICAL_SPREAD = {min = -0.2, max = 0.2}
	local MAX_DISTANCE = gunInfo[weapon.Name]["MAX_DISTANCE"]
	
	local HEADSHOT_DAMAGE = gunInfo[weapon.Name]["HEADSHOT_DAMAGE"]
	local BODYSHOT_DAMAGE = gunInfo[weapon.Name]["BODYSHOT_DAMAGE"]

	for pellet=1, PELLETS do
		local ran = Random.new()

		local spreadX = ran:NextNumber(HORIZONTAL_SPREAD.min, HORIZONTAL_SPREAD.max)
		local spreadY = ran:NextNumber(VERTICAL_SPREAD.min, VERTICAL_SPREAD.max)

		local direction = (hitPos + Vector3.new(spreadX, spreadY)).Unit
		local displacement = direction * MAX_DISTANCE

		local raycastParams = RaycastParams.new()

		raycastParams.FilterDescendantsInstances = {player.Character}
		raycastParams.IgnoreWater = true
		raycastParams.FilterType = Enum.RaycastFilterType.Exclude

		local direction = (hitPos - origin.Position).Unit
		local spreadDirection = (direction + Vector3.new(spreadX, spreadY, 0)).Unit
		local displacement = spreadDirection * MAX_DISTANCE

		local result = workspace:Raycast(
			origin.Position,
			displacement,
			raycastParams
		)

		local endPos = nil

		if result then
			endPos = result.Position
			local character = result.Instance.Parent
			local humanoid = character:FindFirstChild("Humanoid")

			if humanoid ~= players:GetPlayerFromCharacter(character) then 
				if result.Instance == character.Head then
					humanoid:TakeDamage(HEADSHOT_DAMAGE)
					damageIndicatorEvent:FireClient(player, HEADSHOT_DAMAGE, humanoid)
				else
					humanoid:TakeDamage(BODYSHOT_DAMAGE)
					damageIndicatorEvent:FireClient(player, BODYSHOT_DAMAGE, humanoid)
				end
			end

		else
			endPos = origin.Position + displacement
		end

		-- // TRACERS

		local tracer = Instance.new("Part")
		tracer.Parent = workspace
		tracer.Anchored = true
		tracer.CanCollide = false
		tracer.CanQuery = false
		tracer.Transparency = 0.6
		tracer.Color = Color3.new(0.960784, 0.666667, 0.156863)
		dService:AddItem(tracer, 0.2)

		local tracerLength = (endPos - origin.Position).Magnitude
		tracer.Size = Vector3.new(0.07, 0.07, tracerLength)
		tracer.CFrame = CFrame.lookAt(origin.Position + (endPos - origin.Position) / 2, endPos)
	end
end

------------------------------------------------------------------------

-- // MODULE ITSELF
local shootingModule = {}

local shootFunctions = { -- Call these and index with tool's gun type value.
-- there's A LOT of repetition here :(
	SEMI = function(origin: Part, player: Player, hitPos: Mouse, weapon: Tool)
		Raycasting(origin, player, hitPos, weapon)
	end,
	
	AUTOMATIC = function(origin: Part, player: Player, hitPos: Mouse, weapon: Tool)
		Raycasting(origin, player, hitPos, weapon)
	end,
	
	SHOTGUN = function(origin: Part, player: Player, hitPos: Mouse, weapon: Tool)
		ShotgunRaycasting(origin, player, hitPos, weapon)
	end,
	
	AUTO_SHOTGUN = function(origin: Part, player: Player, hitPos: Mouse, weapon: Tool)
		ShotgunRaycasting(origin, player, hitPos, weapon)
	end,
}

function shootingModule.Shoot(origin: Part, player: Player, hitPos: Mouse, weapon: Tool)
	shootFunctions[weapon.GunType.Value](origin, player, hitPos, weapon)
end

return shootingModule
1 Like

mmmmmm

this is still not fixed

i think i’m gonna bump this idk