--<< Services >>--
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
--<< Modules >>--
local Modules = ReplicatedStorage:WaitForChild("Modules")
local FastCast = require(Modules:WaitForChild("FastCastRedux"))
local PartCache = require(Modules.PartCache)
local WeaponConfigs = require(Modules:WaitForChild("WeaponConfigs"))
--<< Remote Events >>--
local RemoteEvents = ReplicatedStorage:WaitForChild("RemoteEvents")
local ClientReplication = RemoteEvents:WaitForChild("ClientReplication")
local HitRegEvent =RemoteEvents:WaitForChild("HitRegEvent")
--<< All Projectile Handler >>--
local workspaceProjectiles = workspace:WaitForChild("Projectiles")
--<< Projectile Types >>--
local Projectiles = ReplicatedStorage:WaitForChild("Projectiles")
local Bullet = Projectiles:WaitForChild("Starter SMG")
local Laser = Projectiles:WaitForChild("Rifle")
--<< FastCast Handler >>--
local Caster = FastCast.new()
-- New raycast parameters.
local CastParams = RaycastParams.new()
CastParams.IgnoreWater = true
CastParams.FilterType = Enum.RaycastFilterType.Blacklist
CastParams.FilterDescendantsInstances = {}
--<< PartCache Handling >>--
--// Reminder: If you are using PartCache, don't forget to use CastBehavior.CosmeticBulletProvider and not CosmeticBulletContainer
--// Notes: This segment uses the caches below to create projectiles for other clients to visualize.
local bulletCache = PartCache.new(Bullet, 100, workspaceProjectiles)
local laserCache = PartCache.new(Laser, 100, workspaceProjectiles)
--<< CastBehavior >>--
local CastBehavior = FastCast.newBehavior()
CastBehavior.RaycastParams = CastParams
CastBehavior.HighFidelityBehavior = FastCast.HighFidelityBehavior.Default
--// Create entry in table for the weapon that you are creating, giving it a range and the bulletType (cache) it is using.
local WeaponSetupTable = {
["Starter SMG"] = {
Range = WeaponConfigs["Starter SMG"]["Bullet Max Distance"],
BulletType = bulletCache
},
["Starter Pistol"] = {
Range = WeaponConfigs["Starter Pistol"]["Bullet Max Distance"],
BulletType = bulletCache
},
["Rifle"] = {
Range = WeaponConfigs["Rifle"]["Bullet Max Distance"],
BulletType = bulletCache
},
}
local ClientProjectileReplication = {}
function ClientProjectileReplication.fireProjectile(projectileData, origin, direction, velocity)
CastParams.FilterDescendantsInstances = {projectileData.player.Character, workspaceProjectiles}
for weaponName, _ in pairs(WeaponSetupTable) do
if weaponName == projectileData.weapon then
CastBehavior.MaxDistance = WeaponSetupTable[projectileData.weapon].Range
CastBehavior.CosmeticBulletProvider = WeaponSetupTable[projectileData.weapon].BulletType
local cast = Caster:Fire(
origin,
direction,
velocity,
CastBehavior
)
cast.UserData.Player = projectileData.player
end
end
end
function OnRayHit(cast, raycastResult, segmentVelocity, cosmeticBulletObject)
local playerWhoFired = cast.UserData.Player
-- This function will be connected to the Caster's "RayHit" event.
local hitPart = raycastResult.Instance
local hitPoint = raycastResult.Position
local normal = raycastResult.Normal
if hitPart ~= nil and hitPart.Parent ~= nil then -- Test if we hit something
local humanoid = hitPart.Parent:FindFirstChildOfClass("Humanoid") -- Is there a humanoid?
if hitPart.Parent:IsA("Accoutrement") then
humanoid = hitPart.Parent.Parent:FindFirstChild("Humanoid")
end
if humanoid then
if humanoid.Parent:FindFirstChild("ForceField") then
HitRegEvent:Fire(playerWhoFired, "Forcefield")
else
HitRegEvent:Fire(playerWhoFired, "Hit")
end
end
end
end
function OnRayUpdated(cast, segmentOrigin, segmentDirection, length, segmentVelocity, cosmeticBulletObject)
-- Whenever the caster steps forward by one unit, this function is called.
-- The bullet argument is the same object passed into the fire function.
if cosmeticBulletObject == nil then
return
end
local bulletLength = cosmeticBulletObject.Size.Z / 2 -- This is used to move the bullet to the right spot based on a CFrame offset
local baseCFrame = CFrame.new(segmentOrigin, segmentOrigin + segmentDirection)
cosmeticBulletObject.CFrame = baseCFrame * CFrame.new(0, 0, -(length - bulletLength))
local trail = cosmeticBulletObject:FindFirstChild("Trail")
if trail then
trail.Enabled = true
end
end
function OnRayTerminated(cast)
local cosmeticBullet = cast.RayInfo.CosmeticBulletObject
if cosmeticBullet ~= nil then
-- This code here is using an if statement on CastBehavior.CosmeticBulletProvider so that the example gun works out of the box.
-- In your implementation, you should only handle what you're doing (if you use a PartCache, ALWAYS use ReturnPart. If not, ALWAYS use Destroy.
if CastBehavior.CosmeticBulletProvider ~= nil then
local trail = cosmeticBullet:FindFirstChild("Trail")
if trail then
trail.Enabled = false
end
CastBehavior.CosmeticBulletProvider:ReturnPart(cosmeticBullet)
else
cosmeticBullet:Destroy()
end
end
end
Caster.RayHit:Connect(OnRayHit)
Caster.LengthChanged:Connect(OnRayUpdated)
Caster.CastTerminating:Connect(OnRayTerminated)
ClientReplication.OnClientEvent:Connect(ClientProjectileReplication.fireProjectile)
return ClientProjectileReplication
So I have this module in ReplicatedStorage that replicates bullets to all other clients. The problem is that when firing a weapon and switching to another one I keep getting an error saying "Attempted to return part Laser to the cache, but it's not in-use! Did you call this on the wrong part?" It seems like its resetting the CosmeticProviderContainer everytime I fire the weapon. How can I fix this so that it finishes the cast and then uses the other one? Thanks!