local F=require(script.Parent)local D={}local t=Random.new()local O={}O.__index=O function O.new(F,t)local y=setmetatable({},O)y.Enabled=true y.LightningBolt=F y.MaxSparkCount=t or 10 y.MinSpeed,y.MaxSpeed=4,6 y.MinDistance,y.MaxDistance=3,6 y.MinPartsPerSpark,y.MaxPartsPerSpark=8,10 y.SparksN=0 y.SlotTable={}y.RefIndex=#D+1 D[y.RefIndex]=y return y end function O.Destroy(F)D[F.RefIndex]=nil for D,t in pairs(F.SlotTable)do if t.Parts[1].Parent==nil then F.SlotTable[D]=nil end end F=nil end function RandomVectorOffset(F,D)return((CFrame.lookAt(Vector3.new(),F)*CFrame.Angles(0,0,t:NextNumber(0,2*math.pi)))*CFrame.Angles(math.acos(t:NextNumber(math.cos(D),1)),0,0)).LookVector end;(game:GetService("\082\117\110\083\101\114\118\105\099\101")).Heartbeat:Connect(function()for D,O in pairs(D)do if O.Enabled==true and O.SparksN<O.MaxSparkCount then local D=O.LightningBolt if D.Parts[1].Parent==nil then O:Destroy()return end local y=D.Parts local k=#y local J={}for F=1,#y,1 do if y[F].Transparency<.3 then J[#J+1]=(F-.5)/k end end local E,B if#J~=0 then E,B=math.ceil(J[1]*O.MaxSparkCount),math.ceil(J[#J]*O.MaxSparkCount)end for D=1,t:NextInteger(1,O.MaxSparkCount-O.SparksN),1 do if#J==0 then break end local s={}for F=E,B,1 do if O.SlotTable[F]==nil then s[#s+1]=F end end if#s~=0 then local D=s[t:NextInteger(1,#s)]local E=t:NextNumber(-0.5,.5)local B=((D-.5)+E)/O.MaxSparkCount local L,P=10,1 for F=1,#J,1 do local D=math.abs(J[F]-B)if D<L then L,P=D,math.floor((J[F]*k+.5)+.5)end end local z=y[P]local Q,l={},{}Q.WorldPosition=z.Position+(E*z.CFrame.RightVector)*z.Size.X l.WorldPosition=Q.WorldPosition+RandomVectorOffset(z.CFrame.RightVector,math.pi/4)*t:NextNumber(O.MinDistance,O.MaxDistance)Q.WorldAxis=(l.WorldPosition-Q.WorldPosition).Unit l.WorldAxis=Q.WorldAxis local w=F.new(Q,l,t:NextInteger(O.MinPartsPerSpark,O.MaxPartsPerSpark))w.MinRadius,w.MaxRadius=0,.8 w.AnimationSpeed=0 w.Thickness=z.Size.Y/2 w.MinThicknessMultiplier,w.MaxThicknessMultiplier=1,1 w.PulseLength=.5 w.PulseSpeed=t:NextNumber(O.MinSpeed,O.MaxSpeed)w.FadeLength=.25 local m,r,i=Color3.toHSV(z.Color)w.Color=Color3.fromHSV(m,.5,i)O.SlotTable[D]=w end end end local y=0 for F,D in pairs(O.SlotTable)do if D.Parts[1].Parent~=nil then y=y+1 else O.SlotTable[F]=nil end end O.SparksN=y end end)return O

local LightningBolt = require(script.Parent)

local ActiveSparks = {}


local rng = Random.new()
local LightningSparks = {}
LightningSparks.__index = LightningSparks

function LightningSparks.new(LightningBolt, MaxSparkCount)
	local self = setmetatable({}, LightningSparks)
	
	--Main (default) properties--
	
		self.Enabled = true --Stops spawning sparks when false
		self.LightningBolt = LightningBolt --Bolt which sparks fly out of
		self.MaxSparkCount = MaxSparkCount or 10 --Max number of sparks visible at any given instance
		self.MinSpeed, self.MaxSpeed = 4, 6 --Min and max PulseSpeeds of sparks
		self.MinDistance, self.MaxDistance = 3, 6 --Governs how far sparks travel away from main bolt
		self.MinPartsPerSpark, self.MaxPartsPerSpark = 8, 10 --Adjustable
	
	--
	
	self.SparksN = 0
	self.SlotTable = {}
	self.RefIndex = #ActiveSparks + 1
	
	ActiveSparks[self.RefIndex] = self
	
	return self
end

function LightningSparks:Destroy()
	ActiveSparks[self.RefIndex] = nil
	
	for i, v in pairs(self.SlotTable) do
		if v.Parts[1].Parent == nil then
			self.SlotTable[i] = nil --Removes reference to prevent memory leak
		end
	end
	
	self = nil
end

function RandomVectorOffset(v, maxAngle) --returns uniformly-distributed random unit vector no more than maxAngle radians away from v
    return (CFrame.lookAt(Vector3.new(), v)*CFrame.Angles(0, 0, rng:NextNumber(0, 2*math.pi))*CFrame.Angles(math.acos(rng:NextNumber(math.cos(maxAngle), 1)), 0, 0)).LookVector
end 

game:GetService("RunService").Heartbeat:Connect(function ()
	
	for _, ThisSpark in pairs(ActiveSparks) do
		
		if ThisSpark.Enabled == true and ThisSpark.SparksN < ThisSpark.MaxSparkCount then
			
			local Bolt = ThisSpark.LightningBolt
			
			if Bolt.Parts[1].Parent == nil then
				ThisSpark:Destroy()
				return 
			end
			
			local BoltParts = Bolt.Parts
			local BoltPartsN = #BoltParts
				
			local opaque_parts = {}
			
			for part_i = 1, #BoltParts do --Fill opaque_parts table
				
				if BoltParts[part_i].Transparency < 0.3 then --minimum opacity required to be able to generate a spark there
					opaque_parts[#opaque_parts + 1] = (part_i - 0.5) / BoltPartsN
				end
				
			end
			
			local minSlot, maxSlot 
			
			if #opaque_parts ~= 0 then
				minSlot, maxSlot = math.ceil(opaque_parts[1]*ThisSpark.MaxSparkCount), math.ceil(opaque_parts[#opaque_parts]*ThisSpark.MaxSparkCount)
			end
			
			for _ = 1, rng:NextInteger(1, ThisSpark.MaxSparkCount - ThisSpark.SparksN) do
				
				if #opaque_parts == 0 then break end
				
				local available_slots = {}
				
				for slot_i = minSlot, maxSlot do --Fill available_slots table
					
					if ThisSpark.SlotTable[slot_i] == nil then --check slot doesn't have existing spark
						available_slots[#available_slots + 1] = slot_i
					end
					
				end
				
				if #available_slots ~= 0 then 
					
					local ChosenSlot = available_slots[rng:NextInteger(1, #available_slots)]
					local localTrng = rng:NextNumber(-0.5, 0.5)
					local ChosenT = (ChosenSlot - 0.5 + localTrng)/ThisSpark.MaxSparkCount
					
					local dist, ChosenPart = 10, 1
					
					for opaque_i = 1, #opaque_parts do
						local testdist = math.abs(opaque_parts[opaque_i] - ChosenT)
						if testdist < dist then
							dist, ChosenPart = testdist, math.floor((opaque_parts[opaque_i]*BoltPartsN + 0.5) + 0.5)
						end
					end
					
					local Part = BoltParts[ChosenPart]
					
					--Make new spark--
					
					local A1, A2 = {}, {}
					A1.WorldPosition = Part.Position + localTrng*Part.CFrame.RightVector*Part.Size.X
					A2.WorldPosition = A1.WorldPosition + RandomVectorOffset(Part.CFrame.RightVector, math.pi/4)*rng:NextNumber(ThisSpark.MinDistance, ThisSpark.MaxDistance)
					A1.WorldAxis = (A2.WorldPosition - A1.WorldPosition).Unit
					A2.WorldAxis = A1.WorldAxis
					local NewSpark = LightningBolt.new(A1, A2, rng:NextInteger(ThisSpark.MinPartsPerSpark, ThisSpark.MaxPartsPerSpark))
					
					--NewSpark.MaxAngleOffset = math.rad(70)
					NewSpark.MinRadius, NewSpark.MaxRadius = 0, 0.8
					NewSpark.AnimationSpeed = 0
					NewSpark.Thickness = Part.Size.Y / 2
					NewSpark.MinThicknessMultiplier, NewSpark.MaxThicknessMultiplier = 1, 1
					NewSpark.PulseLength = 0.5
					NewSpark.PulseSpeed = rng:NextNumber(ThisSpark.MinSpeed, ThisSpark.MaxSpeed)
					NewSpark.FadeLength = 0.25
					local cH, cS, cV = Color3.toHSV(Part.Color)
					NewSpark.Color = Color3.fromHSV(cH, 0.5, cV)
					
					ThisSpark.SlotTable[ChosenSlot] = NewSpark
					
					--
					
				end
				
			end
			
		end
		
		
		
		--Update SparksN--
		
		local slotsInUse = 0
		
		for i, v in pairs(ThisSpark.SlotTable) do
			if v.Parts[1].Parent ~= nil then
				slotsInUse = slotsInUse + 1
			else
				ThisSpark.SlotTable[i] = nil --Removes reference to prevent memory leak
			end
		end
		
		ThisSpark.SparksN = slotsInUse
		
		--
	end
	
end)

return LightningSparks
