LadyCelestia

Roblox HitboxMaster module

Aug 22nd, 2023 (edited)
1,533
-1
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 23.32 KB | None | 0 1
  1. --[[
  2.     LadyCelestia - 8/20/2023
  3.     All-purpose calculation-based arbitrary hitbox system
  4. --]]
  5.  
  6. local RunService = game:GetService("RunService")
  7. if RunService:IsClient() == true then
  8.     error("[Hitbox]: You cannot run HitboxMaster on client-side. This module supports server-side only.")
  9.     return nil
  10. end
  11.  
  12. local Hitbox = {}
  13. Hitbox.__index = Hitbox
  14.  
  15. local Debris = game:GetService("Debris")
  16.  
  17. local ServerStorage = game:GetService("ServerStorage")
  18. local Values = ServerStorage:WaitForChild("Values")
  19. local HitboxSerial = Values:WaitForChild("HitboxSerial")
  20.  
  21. local ReplicatedStorage = game:GetService("ReplicatedStorage")
  22. local Modules = ReplicatedStorage:WaitForChild("Modules")
  23. local ScriptSignal = require(Modules:WaitForChild("ScriptSignal"))
  24. local CustomGlobals = require(Modules:WaitForChild("CustomGlobals"))
  25. local math, string, table = CustomGlobals()
  26.  
  27. Hitbox.new = function(attachment: Attachment)
  28.     --[[
  29.         Creates a Hitbox object that exists in an arbitrary euclidean space
  30.        
  31.        
  32.        
  33.         Module.new(instance or nil Attachment) - Create a new Hitbox object. Returns Hitbox.
  34.         Module:IsHitboxBackstab(Part, HitboxDataBundle) - Determine if the instant hitbox data described by HitboxDataBundle is a 'backstab' against Part. Returns Boolean.
  35.         Module:IsBackstab(Part, Character) - Determine if Character is in a position to 'backstab' Part. Returns Boolean.
  36.        
  37.         ---------[Methods]---------
  38.        
  39.         Hitbox:ChangeAttachment(instance Attachment) - Change the Attachment of the Hitbox. If Hitbox is bound to a valid Attachment afterward, return true. Otherwise return false. Leave argument empty if you want the Hitbox to be unbound.
  40.         Hitbox:ChangeOverlapParams(tuple Parameters) - Change the properties of OverlapParams used in Region3 scanning. Refer to Roblox API. Returns boolean Success.
  41.         Hitbox:AddIgnore(instance Part or Model) - Add an object to OverlapParams' IgnoreList. Returns boolean Success.
  42.         Hitbox:RemoveIgnore(instance Part or Model) - Remove an object from OverlapParams' IgnoreList. Returns integer RemovedAmount.
  43.         Hitbox:Destroy() - Completely erase the Hitbox object, halts all its operations, stop visualization and disconnect all its ScriptConnections. Hitbox becomes an empty table {}. Residue data will be unaccessible and will be garbage collected shortly. Returns boolean Success.
  44.         Hitbox:Visualize() - Visualize the Hitbox in transparent red. Visualization updates every 5 frames (can be wobbly). POORLY OPTIMIZED, STRICTLY FOR TESTING USES ONLY!
  45.         Hitbox:Unvisualize() - Unvisualize the Hitbox.
  46.        
  47.        
  48.         ---------[Wrapper Methods]---------
  49.         Wrapper methods for composition OOP.
  50.        
  51.         Hitbox:Activate() - Activate Hitbox's hit scanning.
  52.         Hitbox:Deactivate() - Deactivate Hitbox's hit scanning.
  53.         Hitbox:IsActive() - Get whether the Hitbox is active or not. Returns Boolean.
  54.         Hitbox:IsConstructed() - Get whether the Hitbox is constructed or not. Returns Boolean.
  55.         Hitbox:IsAttachment() - Get whether the Hitbox is using Attachment mode or not. Returns Boolean.
  56.         Hitbox:GetSerial() - Get the Serial number of this Hitbox. Returns Integer.
  57.         Hitbox:GetCurrentSerial() - Get the current highest Serial number of all Hitboxes. Returns Integer.
  58.         Hitbox:GetDebounce() - Get the Debounce of the Hitbox. Returns Integer.
  59.         Hitbox:GetMode() - Get the mode of the Hitbox's construction. Returns String or nil.
  60.        
  61.        
  62.         ---------[Construction methods]---------
  63.        
  64.         Hitbox:Disconnect() - Disconnects Hitbox.Connection. Return boolean Success.
  65.         Hitbox:DisconnectHit() - Disconnects all Hitbox.Hit ScriptConnections. Return boolean Success.
  66.         Hitbox:ConstructLinear({
  67.             number Velocity, - Studs per Second. Required.
  68.             vector3 Unit, - Directional vector of the path. Required.
  69.             vector3 Position, - Can be used in place of Unit (if unit is nil), second position to calculate the Unit from. Required if Unit is nil.
  70.             tuple IgnoreList, - A list of parts that are to be ignored by the Hitbox. Default {}
  71.             boolean RespectCanCollide, - If true, parts that are CanCollide == false are ignored by the Hitbox. Default false.
  72.             integer MaxParts, - How many parts can be scanned by the Hitbox each frame. Default infinite.
  73.             string CollisionGroup, - If not nil, only parts in the selected CollisionGroup are not ignored by the Hitbox. Default nil.
  74.         }) - Sets Mode to Linear and construct a linear trajectory for the Hitbox. Return boolean Success.
  75.        
  76.         Hitbox:ConstructBezier({
  77.             string Mode, - Quadratic or Cubic. Algorithm of the curve. Required.
  78.             number Velocity, - Studs per Second. Required.
  79.             vector3 Start, - Start point of the curve. Required.
  80.             vector3 End, - End point of the curve. Required.
  81.             vector3 Control1, - Control point of the curve. Required.
  82.             vector3 Control2, - Control point of the curve. Required if Cubic mode.
  83.         }) - Return boolean Success.
  84.        
  85.         Hitbox:Deconstruct(boolean Bypass) - Only usable if Active == false. Deconstruct all Hitbox trajectories (if any), allowing construction of new trajectories. Does not reset Attachment usage. If Bypass is true, deconstruct regardless of limitations. Return boolean Success.
  86.        
  87.        
  88.         ---------[Editable Variables]---------
  89.        
  90.         External Variables (Can be changed by other scripts)
  91.         -=[+++]=-
  92.         Hitbox.Active (Boolean) - Whether the Hitbox is active. True means the Hitbox can detect hits, false is otherwise. Default false.
  93.         When Hitbox is Active, its Position will move based on its constructed trajectory and mode. If there is no construction, the Hitbox will stay still.
  94.         If UseAttachment is true, Attachment mode will be active if Hitbox is activated while no trajectory is constructed. The Hitbox's position will snap to the position of the adorned Attachment each frame in Attachment mode.
  95.         Every frame while the Hitbox is Active, deltaTime of each server frame (Stepped) will be subtracted from Time.
  96.        
  97.         Hitbox.Position (Vector3) - The current location of the Hitbox. You would want to set it to your desired starting point for Linear and Bezier mode. Default Vector3.new(0, 0, 0)
  98.         Hitbox.Shape (String - Sphere or Box) - The shape of the Hitbox. Sphere uses Radius. Box uses Size. Default Sphere.
  99.         Hitbox.Radius (Integer) - How big the Hitbox is in stud. Spherical radius. Applies only when shape is Sphere. Default 3.
  100.         Hitbox.Size (Vector3) - The dimension of the Hitbox in Vector3. Applies only when shape is Box. Default Vector3.new(3, 3, 3).
  101.         Hitbox.Orientation (Vector3) - An optional orientation added to the Hitbox if Hitbox.Shape is Box. Takes priority. Overrides default.
  102.         Hitbox.CopyCFrame (BasePart) - An optional part whose positional CFrame is copied by the Hitbox if Hitbox.Shape is Box. Takes second priority. Overrides default.
  103.         Hitbox.Pierce (Integer) - How many targets can be hit in total by the Hitbox. Default 1.
  104.         Hitbox.Debounce (Integer) - How many seconds of immunity to this Hitbox are given to Humanoids hit. Default 5.
  105.         Hitbox.Time (Number) - How much lifespan the Hitbox has left before despawning in seconds. Default 1.
  106.         When Time hits <= 0, the Hitbox despawns and all its Hit connections disconnect as well as its RunService connection. The Hitbox object is rendered dead and should not be referenced anymore in order to be queued for Garbage Collection.
  107.         If Hitbox.Mode is Bezier, the Hitbox will not despawn when Time hits <= 0. Instead, the Hitbox despawns when its Bezier trajectory is traversed.
  108.        
  109.        
  110.         ---------[ScriptSignal]---------
  111.        
  112.         Hitbox.Hit - A ScriptSignal that can be connected to.
  113.         Hitbox.Hit:Connect(function(Humanoid, HitPart, HitboxDataBundle)) - Creates a ScriptConnection that's fired everytime a valid hit is registered by the Hitbox. Returns ScriptConnection.
  114.         Example:
  115.         local ScriptConnection = Hitbox.Hit:Connect(function(Humanoid, HitPart, HitboxDataBundle)
  116.             print(Humanoid.Parent.Name .. " has been hit!")
  117.             if Module:IsHitboxBackstab(HitPart, HitboxDataBundle) == true then
  118.                 print("It is a backstab!")
  119.                 Humanoid:TakeDamage(15)
  120.             else
  121.                 Humanoid:TakeDamage(10)
  122.             end
  123.         end)
  124.         pcall(function()
  125.             ScriptConnection:Disconnect()
  126.         end)
  127.        
  128.         class HitboxDataBundle - A class returned by Hitbox.Hit, used for Module:IsHitboxBackstab(Part, HitboxDataBundle).
  129.        
  130.         ALWAYS wrap ScriptConnection:Disconnect() inside a pcall, as DisconnectHit, Hitbox:Deconstruct() and Hitbox:Destroy() will disconnect all ScriptConnections.
  131.        
  132.        
  133.         ---------[Internal Variables]---------
  134.        
  135.         Internal Variables (Do not change with other scripts)
  136.         -=[+++]=-
  137.         Hitbox.Serial (Integer) - A numeral identifier of the hitbox. Doesn't need to be changed.
  138.         Hitbox.Constructed (Boolean) - An internal indicator to if there is a path constructed for the Hitbox.
  139.         Hitbox.UseAttachment (Boolean) - Internal indicator whether an attachment is used or not.
  140.         Hitbox.Attachment (Instance) - The adorned Attachment.
  141.         Hitbox.Mode (String) - The construction mode of the Hitbox.
  142.         Modes:
  143.         Linear - Goes in a straight line based on an Unit at a constant Velocity.
  144.         Bezier - Goes in a curve based on Bezier Points and a Bezier Equation at a constant Velocity. Hitbox doesn't despawn when running out of time, but despawns when its Bezier trajectory is traversed.
  145.         Attachment - The position of the adorned Attachment is the position of the Hitbox.
  146.        
  147.         Hitbox.Signal (RBXScriptSignal) - The RunService signal that is connected to.
  148.         Hitbox.Connection (RBXScriptConnection) - The RunService connection that runs every server frame.
  149.        
  150.        
  151.         Linear Mode Variables (Internal)
  152.         -=[+++]=-
  153.         Hitbox.Unit (Vector3) - A directional vector.
  154.         Hitbox.Velocity (Number) - How fast the Hitbox travels in Studs per Second.
  155.         Hitbox.OverlapParams (OverlapParams) - OverlapParams for Region3 scanning.
  156.        
  157.        
  158.         Bezier Mode Variables (Internal)
  159.         -=[+++]=-
  160.         Hitbox.BezierMode (String) - Quadratic or Cubic algorithm.
  161.         Hitbox.BezierLength (Number) - Approximate length of the curve in studs.
  162.         Hitbox.Velocity (Number) - How fast the Hitbox travels in Studs per Second.
  163.         Hitbox.BezierCompletion (Number) - An indicator between 0 and 1 showing where the Hitbox is along the curve.
  164.         Hitbox.StartPoint (Vector3) - Starting point of the curve.
  165.         Hitbox.ControlPoint1 (Vector3) - Control point.
  166.         Hitbox.ControlPoint2 (Vector3?) - Control point. Used only in Cubic BezierMode.
  167.         Hitbox.EndPoint (Vector3) - End point of the curve.
  168.     --]]
  169.    
  170.    
  171.    
  172.     HitboxSerial.Value += 1
  173.    
  174.     local self = setmetatable(
  175.         {
  176.             Serial = HitboxSerial.Value,
  177.             Active = false,
  178.             Constructed = false,
  179.             Position = Vector3.new(0, 0, 0),
  180.             Shape = "Sphere",
  181.             Radius = 3,
  182.             Size = Vector3.new(3, 3, 3),
  183.             Pierce = 1,
  184.             Debounce = 5,
  185.             Time = 1,
  186.             Signal = RunService.Stepped,
  187.             Hit = ScriptSignal.new(),
  188.         },
  189.         Hitbox
  190.     )
  191.    
  192.     local frame = 0
  193.     local warned = false
  194.     self.Connection = self.Signal:Connect(function(_, deltaTime)
  195.        
  196.         frame += 1
  197.        
  198.         if self:IsActive() == true then
  199.            
  200.             self.Time -= deltaTime
  201.            
  202.             if self.Mode == "Linear" then
  203.                
  204.                 self.Position += (self.Unit * (self.Velocity * deltaTime))
  205.                
  206.             elseif self.Mode == "Bezier" then
  207.                
  208.                 local interpolationGain = (self.Velocity * deltaTime) / self.BezierLength
  209.                 if interpolationGain > (1 - self.BezierCompletion) then
  210.                     interpolationGain = (1 - self.BezierCompletion)
  211.                 end
  212.                 self.BezierCompletion += interpolationGain
  213.                
  214.                 if self.BezierMode == "Quadratic" then
  215.                     self.Position = math.quadbez(self.StartPoint, self.ControlPoint1, self.EndPoint, self.BezierCompletion)
  216.                    
  217.                 elseif self.BezierMode == "Cubic" then
  218.                     self.Position = math.cubicbez(self.StartPoint, self.ControlPoint1, self.ControlPoint2, self.EndPoint, self.BezierCompletion)
  219.                    
  220.                 end
  221.                
  222.             elseif self:IsAttachment() == true and self.Attachment ~= nil then
  223.                
  224.                 self.Position = self.Attachment.WorldCFrame.Position
  225.                
  226.             end
  227.            
  228.             if frame >= 5 and self.Visual ~= nil then
  229.                 --print(self.Serial, self.Radius, self.Pierce, self.Debounce, self.Time)
  230.                 frame = 0
  231.                
  232.                 if self.Shape == "Sphere" then
  233.                     self.Visual.Shape = Enum.PartType.Ball
  234.                     self.Visual.Size = Vector3.new(self.Radius * 2, self.Radius * 2, self.Radius * 2)
  235.                 else
  236.                     self.Visual.Shape = Enum.PartType.Block
  237.                     self.Visual.Size = self.Size
  238.                 end
  239.                
  240.                 if self.Position ~= nil then
  241.                     self.Visual.Position = self.Position
  242.                 end
  243.                
  244.             end
  245.            
  246.             if self.Pierce > 0 then
  247.                
  248.                 local result: {BasePart} = {}
  249.                 if self.Shape == "Sphere" then
  250.                     result = workspace:GetPartBoundsInRadius(self.Position, self.Radius, self.OverlapParams) or {}
  251.                 elseif self.Shape == "Box" then
  252.                     if self.Orientation ~= nil then
  253.                         result = workspace:GetPartBoundsInBox(CFrame.new(self.Position) * CFrame.Angles(math.rad(self.Orientation.X), math.rad(self.Orientation.Y), math.rad(self.Orientation.Z)), self.Size, self.OverlapParams) or {}
  254.                     elseif self.CopyCFrame ~= nil then
  255.                         result = workspace:GetPartBoundsInBox(self.CopyCFrame.CFrame, self.Size, self.OverlapParams) or {}
  256.                     else
  257.                         result = workspace:GetPartBoundsInBox(CFrame(self.Position), self.Size, self.OverlapParams) or {}
  258.                     end
  259.                 elseif frame == 5 and warned == false then
  260.                     warned = true
  261.                     warn("[Hitbox]: Hitbox serial " ..self.Serial.. " has an invalid shape.")
  262.                 end
  263.                 if #result > 0 then
  264.                     local hitHumanoids = {}
  265.                     local registeredHumanoids: {Humanoid} = {}
  266.  
  267.                     --Placeholder
  268.                     for _,v in ipairs(result) do
  269.                         local hum = v.Parent:FindFirstChildOfClass("Humanoid")
  270.                         if hum then
  271.                             if v.Parent:FindFirstChildOfClass("ForceField") == nil and v.Parent:FindFirstChild("HitboxSerial" .. self:GetSerial()) == nil then
  272.                                 table.insert(hitHumanoids, {hum, v})
  273.                             end
  274.                         end
  275.                     end
  276.                     --Placeholder end
  277.                    
  278.                     for _,v in pairs(hitHumanoids) do
  279.                        
  280.                         local canHit: boolean = true
  281.                         for _,v2 in ipairs(registeredHumanoids) do
  282.                             if v[1] == v2 then
  283.                                 canHit = false
  284.                                 break
  285.                             end
  286.                         end
  287.                        
  288.                         if canHit == true then
  289.                            
  290.                             if self:GetDebounce() > 0 then
  291.                                 local newSerial: BoolValue = Instance.new("BoolValue")
  292.                                 Debris:AddItem(newSerial, self:GetDebounce())
  293.                                 newSerial.Name = "HitboxSerial" .. self:GetSerial()
  294.                                 newSerial.Value = true
  295.                                 newSerial.Parent = v.Parent
  296.                             end
  297.                            
  298.                             self.Hit:Fire(v[1], v[2], {
  299.                                 ["Serial"] = self.Serial,
  300.                                 ["Mode"] = self.Mode,
  301.                                 ["Attachment"] = self.Attachment or false,
  302.                                 ["Position"] = self.Position,
  303.                                 ["Radius"] = self.Radius or 0,
  304.                                 ["Size"] = self.Size or Vector3.new(0, 0, 0),
  305.                                 ["Pierce"] = self.Pierce - 1
  306.                             })
  307.                             table.insert(registeredHumanoids, v[1])
  308.  
  309.                             self.Pierce -= 1
  310.                             if self.Pierce <= 0 then
  311.                                 break
  312.                             end
  313.                            
  314.                         end
  315.                        
  316.                     end
  317.  
  318.                 end
  319.                
  320.             end
  321.            
  322.             if self.Time <= 0 and self.Mode ~= "Bezier" then
  323.                 self:Destroy()
  324.             end
  325.            
  326.             if self.Mode == "Bezier" then
  327.                 if self.BezierCompletion >= 1 then
  328.                     self:Destroy()
  329.                 end
  330.             end
  331.  
  332.         end
  333.     end)
  334.    
  335.     function self:Visualize()
  336.        
  337.         if self.Visual ~= nil then
  338.             warn("[Hitbox]: Hitbox is already visualizing.")
  339.             return nil
  340.         end
  341.        
  342.         self.Visual = Instance.new("Part")
  343.         self.Visual.Name = "HitboxVisualization" .. tostring(self:GetSerial())
  344.         self.Visual.Anchored = true
  345.         self.Visual.CanCollide = false
  346.         self.Visual.BrickColor = BrickColor.new("Really red")
  347.         self.Visual.Transparency = 0.75
  348.         self.Visual.Material = Enum.Material.SmoothPlastic
  349.         self.Visual.Position = self.Position or Vector3.new(0, 0, 0)
  350.        
  351.         if self.Shape == "Sphere" then
  352.             self.Visual.Shape = Enum.PartType.Ball
  353.             self.Visual.Size = Vector3.new(self.Radius * 2, self.Radius * 2, self.Radius * 2)
  354.         else
  355.             self.Visual.Shape = Enum.PartType.Block
  356.             self.Visual.Size = self.Size
  357.         end
  358.        
  359.         self.Visual.Parent = workspace
  360.        
  361.         return self.Visual
  362.        
  363.     end
  364.    
  365.     function self:Unvisualize()
  366.        
  367.         if self.Visual == nil then
  368.             warn("[Hitbox]: Hitbox is not visualizing.")
  369.             return false
  370.         end
  371.        
  372.         self.Visual:Destroy()
  373.         self.Visual = nil
  374.        
  375.         return true
  376.        
  377.     end
  378.    
  379.     function self:Activate()
  380.         --print("hitbox activated. serial: " ..self.Serial)
  381.         self.Active = true
  382.     end
  383.    
  384.     function self:Deactivate()
  385.         self.Active = false
  386.     end
  387.    
  388.     function self:IsActive()
  389.         return self.Active or false
  390.     end
  391.    
  392.     function self:IsConstructed()
  393.         return self.Constructed or false
  394.     end
  395.    
  396.     function self:IsAttachment()
  397.         return self.UseAttachment or false
  398.     end
  399.    
  400.     function self:GetSerial()
  401.         return self.Serial
  402.     end
  403.    
  404.     function self:GetCurrentSerial()
  405.         return HitboxSerial.Value
  406.     end
  407.    
  408.     function self:GetDebounce()
  409.         return self.Debounce
  410.     end
  411.    
  412.     function self:GetMode()
  413.         return self.Mode or nil
  414.     end
  415.    
  416.     function self:AddIgnore(object: Instance)
  417.         --print("hitbox ignore added. serial: " ..self.Serial)
  418.         if typeof(object) == "Instance" then
  419.             if object:IsA("BasePart") or object:IsA("Model") then
  420.                 local oldList = self.OverlapParams.FilterDescendantsInstances or {}
  421.                 table.insert(oldList, object)
  422.                 self.OverlapParams.FilterDescendantsInstances = oldList
  423.                 return true
  424.             end
  425.         end
  426.        
  427.         return false
  428.        
  429.     end
  430.    
  431.     function self:RemoveIgnore(object: Instance)
  432.        
  433.         if typeof(object) == "Instance" then
  434.             if object:IsA("BasePart") or object:IsA("Model") then
  435.                
  436.                 local indexes: {number} = {}
  437.                 local oldList = self.OverlapParams.FilterDescendantsInstances
  438.                 for i,v in ipairs(oldList) do
  439.                     if v == object then
  440.                         table.insert(indexes, i)
  441.                     end
  442.                 end
  443.                
  444.                 for i,v in ipairs(indexes) do
  445.                     table.remove(oldList, v)
  446.                     for i2,v2 in ipairs(indexes) do
  447.                         if i2 > i and v2 > v then
  448.                             indexes[i2] -= 1
  449.                         end
  450.                     end
  451.                 end
  452.                 self.OverlapParams.FilterDescendantsInstances = oldList
  453.                
  454.                 return #indexes
  455.                
  456.             end
  457.         end
  458.        
  459.         return 0
  460.        
  461.     end
  462.    
  463.     function self:ChangeOverlapParams(args)
  464.        
  465.         self.OverlapParams.FilterDescendantsInstances = args["IgnoreList"] or self.OverlapParams.FilterDescendantsInstances
  466.         self.OverlapParams.RespectCanCollide = args["RespectCanCollide"] or self.OverlapParams.RespectCanCollide
  467.         self.OverlapParams.MaxParts = args["MaxParts"] or self.OverlapParams.MaxParts
  468.        
  469.         if args["CollisionGroup"] ~= nil then
  470.             self.OverlapParams.CollisionGroup = args["CollisionGroup"]
  471.         end
  472.        
  473.         return true
  474.        
  475.     end
  476.    
  477.     function self:ChangeAttachment(attachment: Attachment)
  478.         --print("hitbox attachment changed. serial: " ..self.Serial)
  479.         self.UseAttachment = pcall(function()
  480.             return attachment:IsA("Attachment")
  481.         end) or false
  482.         self.Attachment = attachment
  483.        
  484.         return self:IsAttachment()
  485.        
  486.     end
  487.    
  488.     function self:Disconnect()
  489.        
  490.         if self.Connection ~= nil then
  491.  
  492.             if typeof(self.Connection) == "RBXScriptConnection" then
  493.                 self.Connection:Disconnect()
  494.             end
  495.  
  496.         end
  497.  
  498.         return true
  499.        
  500.     end
  501.    
  502.     function self:DisconnectAll()
  503.         self.Hit:DisconnectAll()
  504.         return true
  505.     end
  506.    
  507.     function self:ConstructLinear(args)
  508.        
  509.         if self:IsConstructed() == true then
  510.             warn("[Hitbox]: Cannot ConstructLinear because Hitbox trajectory is already constructed.")
  511.             return false
  512.         end
  513.  
  514.         if args["Velocity"] == nil then
  515.             warn("[Hitbox]: ConstructLinear missing argument(s).")
  516.             return false
  517.         end
  518.  
  519.         xpcall(function()
  520.             self.Unit = args["Unit"] or (args["Position"] - self.Position).Unit
  521.         end, function()
  522.             error("[Hitbox]: ConstructLinear missing argument(s).")
  523.         end)
  524.  
  525.         self.Velocity = args["Velocity"]
  526.  
  527.         self.Mode = "Linear"
  528.         self.Constructed = true
  529.  
  530.         return true
  531.  
  532.     end
  533.    
  534.     function self:ConstructBezier(args)
  535.  
  536.         if self:IsConstructed() == true then
  537.             warn("[Hitbox]: Cannot ConstructBezier because Hitbox trajectory is already constructed.")
  538.             return false
  539.         end
  540.  
  541.         if args["Start"] == nil or args["End"] == nil or args["Control1"] == nil then
  542.             warn("[Hitbox]: ConstructBezier missing argument(s).")
  543.             return false
  544.         end
  545.        
  546.         self.StartPoint = args["Start"]
  547.         self.EndPoint = args["End"]
  548.         self.ControlPoint1 = args["Control1"]
  549.         self.Velocity = args["Velocity"]
  550.         self.BezierCompletion = 0
  551.        
  552.         if args["Mode"] == "Quadratic" then
  553.            
  554.             self.BezierMode = "Quadratic"
  555.             self.BezierLength = math.quadbezlen(self.StartPoint, self.ControlPoint1, self.EndPoint)
  556.            
  557.         elseif args["Mode"] == "Cubic" then
  558.            
  559.             if args["Control2"] == nil then
  560.                 warn("[Hitbox]: ConstructBezier missing argument(s).")
  561.                 return false
  562.             end        
  563.            
  564.             self.BezierMode = "Cubic"
  565.             self.ControlPoint2 = args["Control2"]
  566.             self.BezierLength = math.cubicbezlen(self.StartPoint, self.ControlPoint1, self.ControlPoint2, self.EndPoint)
  567.            
  568.         end
  569.        
  570.         self.Position = self.StartPoint
  571.        
  572.         self.Mode = "Bezier"
  573.         self.Constructed = true
  574.        
  575.         return true
  576.  
  577.     end
  578.    
  579.     function self:Deconstruct(bypass: boolean?)
  580.  
  581.         if self:IsActive() == true and (bypass == false or bypass == nil) then
  582.             warn("[Hitbox]: Cannot deconstruct because Hitbox is active.")
  583.             return false
  584.         end
  585.        
  586.         if self:IsConstructed() == false and (bypass == false or bypass == nil) then
  587.             warn("[Hitbox]: Cannot deconstruct because Hitbox has no constructed trajectory.")
  588.             return false
  589.         end
  590.        
  591.         self.Hit:DisconnectAll()
  592.        
  593.         if self.Visual ~= nil then
  594.             pcall(self.Visual.Destroy, self.Visual)
  595.         end
  596.         self.Visual = nil
  597.  
  598.         self.Unit = nil
  599.         self.OverlapParams = nil
  600.         self.Velocity = nil
  601.        
  602.         self.StartPoint = nil
  603.         self.EndPoint = nil
  604.         self.ControlPoint1 = nil
  605.         self.ControlPoint2 = nil
  606.         self.BezierMode = nil
  607.         self.BezierCompletion = nil
  608.         self.BezierLength = nil
  609.  
  610.         self.Mode = nil
  611.         self.Constructed = false
  612.  
  613.         return true
  614.  
  615.     end
  616.    
  617.     function self:Destroy()
  618.         --print("hitbox destroyed. serial: " ..self.Serial)
  619.         self.Active = false
  620.        
  621.         self:Deconstruct(true)
  622.        
  623.         self.Hit = nil
  624.         self = {}
  625.         return true
  626.        
  627.     end
  628.    
  629.     self.OverlapParams = OverlapParams.new()
  630.     self.OverlapParams.FilterType = Enum.RaycastFilterType.Exclude
  631.     self.OverlapParams.FilterDescendantsInstances = {}
  632.     self.OverlapParams.RespectCanCollide = false
  633.     self.OverlapParams.MaxParts = 0
  634.    
  635.     self:ChangeAttachment(attachment)
  636.    
  637.     if self:IsAttachment() == true then
  638.         self.Position = self.Attachment.WorldCFrame.Position
  639.     end
  640.    
  641.     --print("hitbox created. serial: " ..self.Serial)
  642.    
  643.     return self
  644.    
  645. end
  646.  
  647. function Hitbox:IsHitboxBackstab(Part: BasePart, HitboxDataBundle)
  648.    
  649.     if HitboxDataBundle.Radius > 100 or HitboxDataBundle.Size.X > 50 or HitboxDataBundle.Size.Y > 50 or HitboxDataBundle.Size.Z > 50 then
  650.         warn("[Hitbox]: Hitbox is too large to support backstab detection. (Maximum 50 axis-radius)")
  651.         return false
  652.     elseif CFrame.new(HitboxDataBundle.Position):inverse() * Part.CFrame < 0 then
  653.         return true
  654.     end
  655.    
  656.     return false
  657.    
  658. end
  659.  
  660. function Hitbox:IsBackstab(Part: BasePart, Character: Model)
  661.    
  662.     local root: BasePart? = Character:FindFirstChild("HumanoidRootPart")
  663.     if root then
  664.         if root.CFrame:inverse() * Part.CFrame < 0 then
  665.             return true
  666.         end
  667.     end
  668.    
  669.     warn("[Hitbox]: Provided Character has no HumanoidRootPart.")
  670.     return false
  671.    
  672. end
  673.  
  674. return Hitbox
Advertisement
Add Comment
Please, Sign In to add comment