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

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)
	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
