local T={}local s={}s.__index=s function s.__tostring(T)return string.format("\072\105\116\098\111\120 \102\111\114 \105\110\115\116\097\110\099\101 \037\115 \091\037\115\093",T.object.Name,T.object.ClassName)end function T.new(T)return setmetatable({},s)end function s.config(k,T,s,b)k.active=false k.deleted=false k.partMode=false k.debugMode=false k.points={}k.targetsHit={}k.endTime=0 k.OnHit=Signal:Create()k.OnUpdate=Signal:Create()k.raycastParams=RaycastParams.new()if b==true then k.raycastParams.FilterType=Enum.RaycastFilterType.Whitelist else k.raycastParams.FilterType=Enum.RaycastFilterType.Blacklist end k.raycastParams.FilterDescendantsInstances=s or{}k.object=T CollectionService:AddTag(k.object,"\082\097\121\099\097\115\116\077\111\100\117\108\101\077\097\110\097\103\101\100")end function s.SetPoints(k,T,s,b)if T and(T:IsA("\066\097\115\101\080\097\114\116")or T:IsA("\077\101\115\104\080\097\114\116")or T:IsA("\065\116\116\097\099\104\109\101\110\116"))then for s,p in ipairs(s)do if typeof(p)=="\086\101\099\116\111\114\051"then local s={IsAttachment=T:IsA("\065\116\116\097\099\104\109\101\110\116"),RelativePart=T,Attachment=p,LastPosition=nil,group=b,solver=CastVectorPoint}table.insert(k.points,s)end end end end function s.RemovePoints(b,T,s)if T then if T:IsA("\066\097\115\101\080\097\114\116")or T:IsA("\077\101\115\104\080\097\114\116")then for k=1,#b.points,1 do local p=b.points[k]for s,C in ipairs(s)do if typeof(p.Attachment)=="\086\101\099\116\111\114\051"and(p.Attachment==C and p.RelativePart==T)then b.points[k]=nil end end end end end end function s.LinkAttachments(b,T,s)if T:IsA("\065\116\116\097\099\104\109\101\110\116")and s:IsA("\065\116\116\097\099\104\109\101\110\116")then local k=T:FindFirstChild("\071\114\111\117\112")local p={RelativePart=nil,Attachment=T,Attachment0=s;LastPosition=nil,group=k and k.Value,solver=CastLinkAttach}table.insert(b.points,p)end end function s.UnlinkAttachments(s,T)for b,k in ipairs(s.points)do if k.Attachment and k.Attachment==T then table.remove(s.points,b)break end end end function s.seekAttachments(b,T,s)if#b.points<=0 then table.insert(b.raycastParams.FilterDescendantsInstances,workspace.Terrain.Ignore)end for s,k in ipairs(b.object:GetDescendants())do if k:IsA("\065\116\116\097\099\104\109\101\110\116")and k.Name==T then local T=k:FindFirstChild("\071\114\111\117\112")local s={Attachment=k;RelativePart=nil;LastPosition=nil;group=T and T.Value,solver=CastAttachment}table.insert(b.points,s)end end if s then if#b.points<=0 then warn(string.format("\n\091\091\082\065\089\067\065\083\084 \087\065\082\078\073\078\071\093\093\n\078\111 \097\116\116\097\099\104\109\101\110\116\115 \119\105\116\104 \116\104\101 \110\097\109\101 \'\037\115\' \119\101\114\101 \102\111\117\110\100 \105\110 \037\115\046 \078\111 \114\097\121\099\097\115\116\115 \119\105\108\108 \098\101 \100\114\097\119\110\046 \067\097\110 \098\101 \105\103\110\111\114\101\100 \105\102 \121\111\117 \097\114\101 \117\115\105\110\103 \083\101\116\080\111\105\110\116\115\046",T,b.object.Name))else print(string.format("\n\091\091\082\065\089\067\065\083\084 \077\069\083\083\065\071\069\093\093\n\n\067\114\101\097\116\101\100 \072\105\116\098\111\120 \102\111\114 \037\115 - \065\116\116\097\099\104\109\101\110\116\115 \102\111\117\110\100\058 \037\115",b.object.Name,#b.points))end end end function s.Destroy(T)if T.deleted then return end if T.OnHit then T.OnHit:Delete()end if T.OnUpdate then T.OnUpdate:Delete()end T.points=nil T.active=false T.deleted=true end function s.HitStart(s,T)s.active=true if T then assert(type(T)=="\110\117\109\098\101\114","\065\114\103\117\109\101\110\116 \035\049 \109\117\115\116 \098\101 \097 \110\117\109\098\101\114\033")local b=.016666666666667 if T<=b or T==math.huge then T=b end s.endTime=clock()+T end end function s.HitStop(T)if T.deleted then return end T.active=false T.endTime=0 table.clear(T.targetsHit)end function s.PartMode(s,T)s.partMode=T end function s.DebugMode(s,T)s.debugMode=T end return T

local HitboxObject = {}
local Hitbox = {}
Hitbox.__index = Hitbox

function Hitbox:__tostring() 
	return string.format("Hitbox for instance %s [%s]", self.object.Name, self.object.ClassName)
end

function HitboxObject:new()
    return setmetatable({}, Hitbox)
end

function Hitbox:config(object, ignoreList, whitelist)
	self.active = false
	self.deleted = false
	self.partMode = false
	self.debugMode = false
	self.points = {}
	self.targetsHit = {}
	self.endTime = 0
	self.OnHit = Signal:Create()
	self.OnUpdate = Signal:Create()
	self.raycastParams = RaycastParams.new()
	if whitelist == true then
		self.raycastParams.FilterType = Enum.RaycastFilterType.Whitelist
	else
		self.raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
	end
	self.raycastParams.FilterDescendantsInstances = ignoreList or {}
	
	self.object = object
	CollectionService:AddTag(self.object, "RaycastModuleManaged")
end

function Hitbox:SetPoints(object, vectorPoints, groupName)
	if object and (object:IsA("BasePart") or object:IsA("MeshPart") or object:IsA("Attachment")) then
		for _, vectors in ipairs(vectorPoints) do
			if typeof(vectors) == "Vector3" then
				local Point = {
					IsAttachment = object:IsA("Attachment"),
					RelativePart = object, 
					Attachment = vectors,
					LastPosition = nil,
					group = groupName,
					solver = CastVectorPoint
				}
				table.insert(self.points, Point)
			end
		end
	end
end

function Hitbox:RemovePoints(object, vectorPoints)
	if object then
		if object:IsA("BasePart") or object:IsA("MeshPart") then --- for some reason it doesn't recognize meshparts unless I add it in
			for i = 1, #self.points do
				local Point = self.points[i]
				for _, vectors in ipairs(vectorPoints) do
					if typeof(Point.Attachment) == "Vector3" and Point.Attachment == vectors and Point.RelativePart == object then
						self.points[i] = nil
					end
				end
			end
		end
	end
end

function Hitbox:LinkAttachments(primaryAttachment, secondaryAttachment)
	if primaryAttachment:IsA("Attachment") and secondaryAttachment:IsA("Attachment") then
		local group = primaryAttachment:FindFirstChild("Group")
		local Point = {
			RelativePart = nil,
			Attachment = primaryAttachment,
			Attachment0 = secondaryAttachment,
			LastPosition = nil,
			group = group and group.Value,
			solver = CastLinkAttach
		}
		table.insert(self.points, Point)
	end
end

function Hitbox:UnlinkAttachments(primaryAttachment)
	for i, Point in ipairs(self.points) do
		if Point.Attachment and Point.Attachment == primaryAttachment then
			table.remove(self.points, i)
			break
		end
	end
end

function Hitbox:seekAttachments(attachmentName, canWarn)
	if #self.points <= 0 then
		table.insert(self.raycastParams.FilterDescendantsInstances, workspace.Terrain.Ignore)
	end
	for _, attachment in ipairs(self.object:GetDescendants()) do
		if attachment:IsA("Attachment") and attachment.Name == attachmentName then
			local group = attachment:FindFirstChild("Group")
			local Point = {
				Attachment = attachment, 
				RelativePart = nil, 
				LastPosition = nil, 
				group = group and group.Value,
				solver = CastAttachment
			}
			table.insert(self.points, Point)
		end
	end
	
	if canWarn then
		if #self.points <= 0 then
			warn(string.format("\n[[RAYCAST WARNING]]\nNo attachments with the name '%s' were found in %s. No raycasts will be drawn. Can be ignored if you are using SetPoints.",
				attachmentName, self.object.Name)
			)
		else
			print(string.format("\n[[RAYCAST MESSAGE]]\n\nCreated Hitbox for %s - Attachments found: %s", 
				self.object.Name, #self.points)
			)
		end
	end
end

function Hitbox:Destroy()
	if self.deleted then return end
	if self.OnHit then self.OnHit:Delete() end
	if self.OnUpdate then self.OnUpdate:Delete() end
	
	self.points = nil
	self.active = false
	self.deleted = true
end

function Hitbox:HitStart(seconds)
	self.active = true
	
	if seconds then
		assert(type(seconds) == "number", "Argument #1 must be a number!")
		
		local minSeconds = 1 / 60 --- Seconds cannot be under 1/60th
		
		if seconds <= minSeconds or seconds == math.huge then
			seconds = minSeconds
		end
		
		self.endTime = clock() + seconds
	end
end

function Hitbox:HitStop()
	if self.deleted then return end
	
	self.active = false
	self.endTime = 0
	table.clear(self.targetsHit)
end

function Hitbox:PartMode(bool)
	self.partMode = bool
end

function Hitbox:DebugMode(bool)
	self.debugMode = bool
end

return HitboxObject
