local O={}local U={}U.__index=U function U.__tostring(O)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",O.object.Name,O.object.ClassName)end function O.new(O)return setmetatable({},U)end function U.config(m,O,U)m.active=false m.deleted=false m.partMode=false m.debugMode=false m.points={}m.targetsHit={}m.endTime=0 m.OnHit=Signal:Create()m.OnUpdate=Signal:Create()m.raycastParams=RaycastParams.new()m.raycastParams.FilterType=Enum.RaycastFilterType.Blacklist m.raycastParams.FilterDescendantsInstances=U or{}m.object=O CollectionService:AddTag(m.object,"\082\097\121\099\097\115\116\077\111\100\117\108\101\077\097\110\097\103\101\100")end function U.SetPoints(Z,O,U,m)if O and(O:IsA("\066\097\115\101\080\097\114\116")or O:IsA("\077\101\115\104\080\097\114\116")or O:IsA("\065\116\116\097\099\104\109\101\110\116"))then for U,v in ipairs(U)do if typeof(v)=="\086\101\099\116\111\114\051"then local U={IsAttachment=O:IsA("\065\116\116\097\099\104\109\101\110\116");RelativePart=O,Attachment=v,LastPosition=nil;group=m;solver=CastVectorPoint}table.insert(Z.points,U)end end end end function U.RemovePoints(m,O,U)if O then if O:IsA("\066\097\115\101\080\097\114\116")or O:IsA("\077\101\115\104\080\097\114\116")then for Z=1,#m.points,1 do local v=m.points[Z]for U,K in ipairs(U)do if typeof(v.Attachment)=="\086\101\099\116\111\114\051"and(v.Attachment==K and v.RelativePart==O)then m.points[Z]=nil end end end end end end function U.LinkAttachments(m,O,U)if O:IsA("\065\116\116\097\099\104\109\101\110\116")and U:IsA("\065\116\116\097\099\104\109\101\110\116")then local Z=O:FindFirstChild("\071\114\111\117\112")local v={RelativePart=nil;Attachment=O,Attachment0=U;LastPosition=nil,group=Z and Z.Value;solver=CastLinkAttach}table.insert(m.points,v)end end function U.UnlinkAttachments(U,O)for m,Z in ipairs(U.points)do if Z.Attachment and Z.Attachment==O then table.remove(U.points,m)break end end end function U.seekAttachments(m,O,U)if#m.points<=0 then table.insert(m.raycastParams.FilterDescendantsInstances,workspace.Terrain.Ignore)end for U,Z in ipairs(m.object:GetDescendants())do if Z:IsA("\065\116\116\097\099\104\109\101\110\116")and Z.Name==O then local O=Z:FindFirstChild("\071\114\111\117\112")local U={Attachment=Z;RelativePart=nil,LastPosition=nil,group=O and O.Value,solver=CastAttachment}table.insert(m.points,U)end end if U then if#m.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",O,m.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",m.object.Name,#m.points))end end end function U.Destroy(O)if O.deleted then return end if O.OnHit then O.OnHit:Delete()end if O.OnUpdate then O.OnUpdate:Delete()end O.points=nil O.active=false O.deleted=true end function U.HitStart(U,O)U.active=true if O then assert(type(O)=="\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 m=.016666666666667 if O<=m or O==math.huge then O=m end U.endTime=clock()+O end end function U.HitStop(O)if O.deleted then return end O.active=false O.endTime=0 table.clear(O.targetsHit)end function U.PartMode(U,O)U.partMode=O end function U.DebugMode(U,O)U.debugMode=O end return O

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)
	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()
	self.raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
	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
