Advertisement
g14ndev

Projectile System Module

Mar 19th, 2025
116
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 26.73 KB | Gaming | 0 0
  1. --[[
  2.     ProjectileSystem Module
  3.     -------------------------
  4.     This module implements a projectile system.
  5.     Instead of using built-in physics components (like BodyVelocity) for flight,
  6.     we simulate projectile motion using Euler integration and raycasting.
  7.  
  8.     When a projectile hits a target (a character with a Humanoid), the target is pushed back
  9.     in the direction of the projectile's travel (preserving the impact angle),
  10.     and the target is temporarily highlighted in red to indicate a hit before reverting to its original colors.
  11.    
  12.     Additional features include homing projectiles, trail effects, explosion effects, and more.
  13. --]]
  14.  
  15. -- Create a table to hold all projectile-related functions and data.
  16. local ProjectileSystem = {}
  17. ProjectileSystem.__index = ProjectileSystem
  18.  
  19. -- Get required Roblox services.
  20. local Players = game:GetService("Players")         -- Manages players.
  21. local Debris = game:GetService("Debris")           -- Automatically removes objects after a set time.
  22. local RunService = game:GetService("RunService")   -- Provides per-frame update events.
  23. local Workspace = game:GetService("Workspace")     -- The game world.
  24.  
  25. -- Define the gravity vector (pointing down).
  26. local GRAVITY_VECTOR = Vector3.new(0, -Workspace.Gravity, 0)
  27. -- Table to hold all active projectile objects.
  28. local activeProjectiles = {}
  29.  
  30. --------------------------------------------------------------------------------
  31. -- Function: CreateProjectilePart
  32. -- Purpose: Creates and returns a new visual part representing the projectile.
  33. -- Parameter:
  34. --   origin (Vector3) - The starting position for the projectile.
  35. --------------------------------------------------------------------------------
  36. function ProjectileSystem.CreateProjectilePart(origin)
  37.     -- Create a new Part instance.
  38.     local part = Instance.new("Part")
  39.     -- Set a small size (0.5 studs cube).
  40.     part.Size = Vector3.new(0.5, 0.5, 0.5)
  41.     -- Use a red color for the projectile.
  42.     part.Color = Color3.new(1, 0, 0)
  43.     -- Set material to Neon for a glowing appearance.
  44.     part.Material = Enum.Material.Neon
  45.     -- Make the part spherical.
  46.     part.Shape = Enum.PartType.Ball
  47.     -- Position the part at the provided origin.
  48.     part.CFrame = CFrame.new(origin)
  49.     -- Allow physics to move the part.
  50.     part.Anchored = false
  51.     -- Disable collisions to avoid interfering with other objects.
  52.     part.CanCollide = false
  53.     -- Parent the part to the Workspace so it appears in the game.
  54.     part.Parent = Workspace
  55.     -- Name the part "Projectile" for easy identification.
  56.     part.Name = "Projectile"
  57.     return part
  58. end
  59.  
  60. --------------------------------------------------------------------------------
  61. -- Function: ApplyHitEffect
  62. -- Purpose: When a projectile hits a damageable target, this function applies a knockback
  63. --          effect and temporarily changes the target's color to red.
  64. -- Parameters:
  65. --   character (Model)     - The target character model.
  66. --   hitPosition (Vector3) - The position where the projectile hit.
  67. --   proj (table)          - The projectile object (used to determine impact direction).
  68. --------------------------------------------------------------------------------
  69. function ProjectileSystem.ApplyHitEffect(character, hitPosition, proj)
  70.     -- Attempt to find the target's HumanoidRootPart.
  71.     local hrp = character:FindFirstChild("HumanoidRootPart")
  72.     if hrp then
  73.         -- Calculate the push direction as the exact opposite of the projectile's velocity.
  74.         -- This ensures that if the projectile hits at a 45° angle, the target is pushed back along that same angle.
  75.         local pushDir = (proj.Velocity).Unit
  76.         -- Create a BodyVelocity to apply the knockback force.
  77.         local bv = Instance.new("BodyVelocity")
  78.         -- Multiply the push direction by a  constant for a weighty push effect.
  79.         bv.Velocity = pushDir * 10
  80.         -- Set MaxForce high in all directions to ensure the force is applied properly (including vertical).
  81.         bv.MaxForce = Vector3.new(1e5, 1e5, 1e5)
  82.         -- Parent the BodyVelocity to the target's HumanoidRootPart.
  83.         bv.Parent = hrp
  84.         -- Remove the BodyVelocity after 0.5 seconds to allow natural physics to resume.
  85.         Debris:AddItem(bv, 0.5)
  86.     end
  87.  
  88.     -- Create a table to store original colors for all BaseParts in the character.
  89.     local originalColors = {}
  90.     -- Iterate over all descendants of the character.
  91.     for _, part in pairs(character:GetDescendants()) do
  92.         -- Check if the object is a BasePart (i.e., an actual physical part).
  93.         if part:IsA("BasePart") then
  94.             -- Store the original color.
  95.             originalColors[part] = part.Color
  96.             -- Set the part's color to bright red to indicate the hit.
  97.             part.Color = Color3.new(1, 0, 0)
  98.         end
  99.     end
  100.  
  101.     -- After 1 second, revert all parts back to their original colors.
  102.     delay(1, function()
  103.         for part, origColor in pairs(originalColors) do
  104.             -- Ensure the part still exists before reverting its color.
  105.             if part and part.Parent then
  106.                 part.Color = origColor
  107.             end
  108.         end
  109.     end)
  110. end
  111.  
  112. --------------------------------------------------------------------------------
  113. -- Function: FireProjectile
  114. -- Purpose: Fires a projectile with the given parameters and adds it to the active list.
  115. -- Parameters:
  116. --   origin (Vector3)    - The starting position of the projectile.
  117. --   direction (Vector3) - The firing direction (should be a unit vector).
  118. --   speed (number)      - The initial speed in studs per second.
  119. --   damage (number)     - The damage to apply upon impact.
  120. --   lifetime (number)   - Time in seconds before the projectile expires.
  121. --   shooter (Player)    - The player who fired the projectile (to prevent self-damage).
  122. --------------------------------------------------------------------------------
  123. function ProjectileSystem.FireProjectile(origin, direction, speed, damage, lifetime, shooter)
  124.     -- Create a new projectile object represented as a table.
  125.     local proj = {}
  126.     -- Set the starting logical position.
  127.     proj.Position = origin
  128.     -- Compute the initial velocity based on the normalized direction and the given speed.
  129.     proj.Velocity = direction.Unit * (speed or 100)
  130.     -- Set the damage value (default is 10).
  131.     proj.Damage = damage or 10
  132.     -- Set the projectile's lifetime (default is 5 seconds).
  133.     proj.Lifetime = lifetime or 5
  134.     -- Record the time the projectile was fired.
  135.     proj.StartTime = tick()
  136.     -- Store the shooter to avoid self-damage.
  137.     proj.Shooter = shooter
  138.     -- Create the visual projectile part at the origin.
  139.     proj.Part = ProjectileSystem.CreateProjectilePart(origin)
  140.     -- Initialize the curve factor for curved trajectories to 0 by default.
  141.     proj.CurveFactor = 0
  142.     -- Set the bounce count to zero.
  143.     proj.BounceCount = 0
  144.     -- Allow up to 2 bounces before the projectile is destroyed.
  145.     proj.MaxBounces = 2
  146.  
  147.     -- Define the OnHit function to handle collision events.
  148.     proj.OnHit = function(self, hit, hitPosition)
  149.         -- Get the parent of the hit object (typically a character model).
  150.         local character = hit.Parent
  151.         if character then
  152.             -- Check for a Humanoid to determine if the target is damageable.
  153.             local humanoid = character:FindFirstChildOfClass("Humanoid")
  154.             if humanoid then
  155.                 -- If the target is the shooter, do not apply damage.
  156.                 if self.Shooter and character == self.Shooter.Character then
  157.                     -- Skip self-damage.
  158.                 else
  159.                     -- Apply the specified damage to the target.
  160.                     humanoid:TakeDamage(self.Damage)
  161.                     -- Apply the custom hit effect (knockback and red highlight).
  162.                     ProjectileSystem.ApplyHitEffect(character, hitPosition, self)
  163.                     -- Return true to indicate that a damageable target was hit.
  164.                     return true
  165.                 end
  166.             end
  167.         end
  168.         -- Return false if the target is not damageable.
  169.         return false
  170.     end
  171.  
  172.     -- Add this projectile to the active projectile list.
  173.     activeProjectiles[#activeProjectiles + 1] = proj
  174.     return proj
  175. end
  176.  
  177. --------------------------------------------------------------------------------
  178. -- Function: DestroyProjectile
  179. -- Purpose: Removes a projectile from the game and cleans up its references.
  180. -- Parameter:
  181. --   proj (table) - The projectile object to be destroyed.
  182. --------------------------------------------------------------------------------
  183. function ProjectileSystem.DestroyProjectile(proj)
  184.     -- If the visual part exists, destroy it.
  185.     if proj.Part then
  186.         proj.Part:Destroy()
  187.     end
  188.     -- Iterate through the active projectile list in reverse order for safe removal.
  189.     for i = #activeProjectiles, 1, -1 do
  190.         if activeProjectiles[i] == proj then
  191.             table.remove(activeProjectiles, i)
  192.             break
  193.         end
  194.     end
  195. end
  196.  
  197. --------------------------------------------------------------------------------
  198. -- Update Loop: Called every frame to update active projectiles.
  199. -- This loop uses Euler integration to update positions, applies gravity and curve forces,
  200. -- performs raycasts for collision detection, applies bounce logic, and updates projectile appearance.
  201. -- Projectiles that exceed their lifetime are removed.
  202. --------------------------------------------------------------------------------
  203. RunService.Heartbeat:Connect(function(deltaTime)
  204.     for i = #activeProjectiles, 1, -1 do
  205.         local proj = activeProjectiles[i]
  206.         local timeElapsed = tick() - proj.StartTime
  207.         if timeElapsed >= proj.Lifetime then
  208.             ProjectileSystem.DestroyProjectile(proj)
  209.         else
  210.             local oldPos = proj.Position
  211.             -- Calculate new position: newPos = oldPos + velocity * deltaTime + 0.5 * gravity * (deltaTime^2)
  212.             local newPos = oldPos + proj.Velocity * deltaTime + 0.5 * GRAVITY_VECTOR * (deltaTime^2)
  213.             -- Update velocity by adding gravity's effect.
  214.             proj.Velocity = proj.Velocity + GRAVITY_VECTOR * deltaTime
  215.             -- If a curve factor is set, add a lateral force to curve the trajectory.
  216.             if proj.CurveFactor ~= 0 then
  217.                 local lateralForce = Vector3.new(-proj.Velocity.Z, 0, proj.Velocity.X).Unit * proj.CurveFactor * deltaTime
  218.                 proj.Velocity = proj.Velocity + lateralForce
  219.             end
  220.             -- Determine movement direction for raycasting.
  221.             local rayDir = newPos - oldPos
  222.             -- Set up raycast parameters to ignore the shooter and the projectile itself.
  223.             local rayParams = RaycastParams.new()
  224.             rayParams.FilterType = Enum.RaycastFilterType.Exclude
  225.             local filters = {}
  226.             if proj.Shooter and proj.Shooter.Character then
  227.                 table.insert(filters, proj.Shooter.Character)
  228.             end
  229.             table.insert(filters, proj.Part)
  230.             rayParams.FilterDescendantsInstances = filters
  231.             -- Perform the raycast from oldPos in the direction of rayDir.
  232.             local rayResult = Workspace:Raycast(oldPos, rayDir, rayParams)
  233.             if rayResult then
  234.                 -- Collision detected: update position to the hit location.
  235.                 proj.Position = rayResult.Position
  236.                 proj.Part.CFrame = CFrame.new(rayResult.Position)
  237.                 -- Call OnHit; if a damageable target was hit, destroy the projectile immediately.
  238.                 local hitDamageable = proj:OnHit(rayResult.Instance, rayResult.Position)
  239.                 if hitDamageable then
  240.                     ProjectileSystem.DestroyProjectile(proj)
  241.                 elseif proj.BounceCount < proj.MaxBounces then
  242.                     -- Otherwise, if bounces remain, increment the bounce count.
  243.                     proj.BounceCount = proj.BounceCount + 1
  244.                     -- Get the surface normal from the raycast result.
  245.                     local normal = rayResult.Normal
  246.                     -- Reflect the velocity using the standard reflection formula.
  247.                     local reflectedVel = proj.Velocity - 2 * proj.Velocity:Dot(normal) * normal
  248.                     -- Apply a bounce factor (reducing speed on bounce).
  249.                     local newVel = reflectedVel * 0.8
  250.                     -- Clamp new velocity to prevent unnatural gains (especially on vertical surfaces).
  251.                     if newVel.Magnitude > proj.Velocity.Magnitude then
  252.                         newVel = newVel.Unit * proj.Velocity.Magnitude
  253.                     end
  254.                     proj.Velocity = newVel
  255.                     -- Offset the position slightly away from the surface.
  256.                     proj.Position = rayResult.Position + normal * 0.5
  257.                 else
  258.                     -- If maximum bounces have been reached, destroy the projectile.
  259.                     ProjectileSystem.DestroyProjectile(proj)
  260.                 end
  261.             else
  262.                 -- No collision: update the projectile's position normally.
  263.                 proj.Position = newPos
  264.                 proj.Part.CFrame = CFrame.new(newPos)
  265.             end
  266.             -- Update the projectile's appearance (e.g., shrink as it travels).
  267.             ProjectileSystem.UpdateProjectileAppearance(proj)
  268.         end
  269.     end
  270. end)
  271.  
  272. --------------------------------------------------------------------------------
  273. -- Function: CalculateImpactPoint
  274. -- Purpose: Computes the theoretical impact point using a simple ballistic formula.
  275. -- Parameters:
  276. --   origin (Vector3)    - Starting position.
  277. --   direction (Vector3) - Firing direction (unit vector).
  278. --   speed (number)      - Initial speed.
  279. --   gravity (number)    - (Optional) Gravity value; defaults to Workspace.Gravity.
  280. -- Returns:
  281. --   A Vector3 representing the predicted impact point.
  282. --------------------------------------------------------------------------------
  283. function ProjectileSystem.CalculateImpactPoint(origin, direction, speed, gravity)
  284.     gravity = gravity or Workspace.Gravity
  285.     local t = (2 * speed * direction.Y) / gravity
  286.     local horizontalVelocity = Vector3.new(direction.X, 0, direction.Z) * speed
  287.     local horizontalDistance = horizontalVelocity * t
  288.     return origin + horizontalDistance
  289. end
  290.  
  291. --------------------------------------------------------------------------------
  292. -- Function: FireCurvedProjectile
  293. -- Purpose: Fires a projectile with a curved trajectory by setting a constant curve factor.
  294. -- Parameters: Same as FireProjectile, with an extra curveFactor parameter.
  295. --------------------------------------------------------------------------------
  296. function ProjectileSystem.FireCurvedProjectile(origin, direction, speed, damage, curveFactor, lifetime, shooter)
  297.     local proj = ProjectileSystem.FireProjectile(origin, direction, speed, damage, lifetime, shooter)
  298.     proj.CurveFactor = curveFactor or 5
  299.     return proj
  300. end
  301.  
  302. --------------------------------------------------------------------------------
  303. -- Function: SetProjectileColor
  304. -- Purpose: Changes the color of the projectile's visual part.
  305. -- Parameters:
  306. --   proj (table)   - The projectile object.
  307. --   color (Color3) - The desired color.
  308. --------------------------------------------------------------------------------
  309. function ProjectileSystem.SetProjectileColor(proj, color)
  310.     if proj and proj.Part then
  311.         proj.Part.Color = color
  312.     end
  313. end
  314.  
  315. --------------------------------------------------------------------------------
  316. -- Function: FadeProjectile
  317. -- Purpose: Gradually fades the projectile out over a given duration.
  318. -- Parameters:
  319. --   proj (table)         - The projectile object.
  320. --   fadeDuration (number) - Duration of the fade (defaults to projectile's lifetime).
  321. --------------------------------------------------------------------------------
  322. function ProjectileSystem.FadeProjectile(proj, fadeDuration)
  323.     fadeDuration = fadeDuration or proj.Lifetime
  324.     local startTime = tick()
  325.     local connection
  326.     connection = RunService.Heartbeat:Connect(function()
  327.         local elapsed = tick() - startTime
  328.         if proj and proj.Part and proj.Part.Parent then
  329.             proj.Part.Transparency = math.clamp(elapsed / fadeDuration, 0, 1)
  330.             if elapsed >= fadeDuration then
  331.                 connection:Disconnect()
  332.             end
  333.         else
  334.             connection:Disconnect()
  335.         end
  336.     end)
  337. end
  338.  
  339. --------------------------------------------------------------------------------
  340. -- Function: UpdateProjectileAppearance
  341. -- Purpose: Updates the projectile's visual appearance based on distance traveled.
  342. -- For example, it gradually shrinks the projectile.
  343. -- Parameter:
  344. --   proj (table) - The projectile object.
  345. --------------------------------------------------------------------------------
  346. function ProjectileSystem.UpdateProjectileAppearance(proj)
  347.     if proj and proj.Part then
  348.         local distance = (proj.Position - proj.Part.Position).Magnitude
  349.         local newSize = Vector3.new(0.5, 0.5, 0.5) * math.clamp(1 - distance / 100, 0.5, 1)
  350.         proj.Part.Size = newSize
  351.     end
  352. end
  353.  
  354. --------------------------------------------------------------------------------
  355. -- Function: ResetProjectile
  356. -- Purpose: Resets a projectile's state to its initial values.
  357. -- Parameter:
  358. --   proj (table) - The projectile object.
  359. --------------------------------------------------------------------------------
  360. function ProjectileSystem.ResetProjectile(proj)
  361.     if proj and proj.Part then
  362.         proj.Position = proj.Origin or proj.Position
  363.         proj.Velocity = Vector3.new(0, 0, 0)
  364.         proj.StartTime = tick()
  365.         proj.Part.CFrame = CFrame.new(proj.Position)
  366.     end
  367. end
  368.  
  369. --------------------------------------------------------------------------------
  370. -- Function: BounceProjectile
  371. -- Purpose: Simulates a bounce by reflecting the projectile's velocity.
  372. -- Parameters:
  373. --   proj (table)         - The projectile object.
  374. --   bounceFactor (number) - Multiplier for the reflected velocity (default 0.8).
  375. --------------------------------------------------------------------------------
  376. function ProjectileSystem.BounceProjectile(proj, bounceFactor)
  377.     bounceFactor = bounceFactor or 0.8
  378.     if proj and proj.Part then
  379.         local normal = Vector3.new(0, 1, 0)
  380.         proj.Velocity = (proj.Velocity - 2 * proj.Velocity:Dot(normal) * normal) * bounceFactor
  381.     end
  382. end
  383.  
  384. --------------------------------------------------------------------------------
  385. -- Function: ExtendLifetime
  386. -- Purpose: Increases the lifetime of a projectile.
  387. -- Parameters:
  388. --   proj (table)      - The projectile object.
  389. --   extraTime (number)- Additional seconds to add.
  390. --------------------------------------------------------------------------------
  391. function ProjectileSystem.ExtendLifetime(proj, extraTime)
  392.     if proj then
  393.         proj.Lifetime = proj.Lifetime + extraTime
  394.     end
  395. end
  396.  
  397. --------------------------------------------------------------------------------
  398. -- Function: SlowProjectile
  399. -- Purpose: Reduces the projectile's speed by applying a slow factor.
  400. -- Parameters:
  401. --   proj (table)       - The projectile object.
  402. --   slowFactor (number)- Factor to multiply the velocity (default 0.5).
  403. --------------------------------------------------------------------------------
  404. function ProjectileSystem.SlowProjectile(proj, slowFactor)
  405.     slowFactor = slowFactor or 0.5
  406.     if proj then
  407.         proj.Velocity = proj.Velocity * slowFactor
  408.     end
  409. end
  410.  
  411. --------------------------------------------------------------------------------
  412. -- Function: ApplyAirResistance
  413. -- Purpose: Simulates drag by gradually reducing the projectile's velocity.
  414. -- Parameters:
  415. --   proj (table)             - The projectile object.
  416. --   resistanceFactor (number)- Factor to multiply the velocity (default 0.99).
  417. --------------------------------------------------------------------------------
  418. function ProjectileSystem.ApplyAirResistance(proj, resistanceFactor)
  419.     resistanceFactor = resistanceFactor or 0.99
  420.     if proj then
  421.         proj.Velocity = proj.Velocity * resistanceFactor
  422.     end
  423. end
  424.  
  425. --------------------------------------------------------------------------------
  426. -- Function: DistanceTravelled
  427. -- Purpose: Calculates the distance the projectile has traveled.
  428. -- Parameter:
  429. --   proj (table) - The projectile object.
  430. -- Returns:
  431. --   A number representing the travel distance.
  432. --------------------------------------------------------------------------------
  433. function ProjectileSystem.DistanceTravelled(proj)
  434.     if proj and proj.Part then
  435.         return (proj.Part.Position - proj.Position).Magnitude
  436.     end
  437.     return 0
  438. end
  439.  
  440. --------------------------------------------------------------------------------
  441. -- Function: GetProjectileTrajectory
  442. -- Purpose: Computes a predicted path for the projectile using Euler integration.
  443. -- Parameters:
  444. --   proj (table)   - The projectile object.
  445. --   steps (number) - Number of simulation steps (default 20).
  446. --   deltaTime (number) - Time interval per step (default 0.1 seconds).
  447. -- Returns:
  448. --   A table of Vector3 positions along the predicted trajectory.
  449. --------------------------------------------------------------------------------
  450. function ProjectileSystem.GetProjectileTrajectory(proj, steps, deltaTime)
  451.     steps = steps or 20
  452.     deltaTime = deltaTime or 0.1
  453.     local trajectory = {}
  454.     local pos = proj.Position
  455.     local vel = proj.Velocity
  456.     for i = 1, steps do
  457.         pos = pos + vel * deltaTime + 0.5 * GRAVITY_VECTOR * (deltaTime^2)
  458.         vel = vel + GRAVITY_VECTOR * deltaTime
  459.         table.insert(trajectory, pos)
  460.     end
  461.     return trajectory
  462. end
  463.  
  464. --------------------------------------------------------------------------------
  465. -- Function: PrintProjectileInfo
  466. -- Purpose: Outputs detailed debug info for a projectile to the output console.
  467. -- Parameter:
  468. --   proj (table) - The projectile object.
  469. --------------------------------------------------------------------------------
  470. function ProjectileSystem.PrintProjectileInfo(proj)
  471.     if proj and proj.Part then
  472.         print("Projectile Info:")
  473.         print("  Position: ", proj.Position)
  474.         print("  Velocity: ", proj.Velocity)
  475.         print("  Damage: ", proj.Damage)
  476.         print("  Time Elapsed: ", tick() - proj.StartTime)
  477.         print("  Bounce Count: ", proj.BounceCount)
  478.     end
  479. end
  480.  
  481. --------------------------------------------------------------------------------
  482. -- Function: DebugProjectiles
  483. -- Purpose: Prints debug info for all active projectiles.
  484. --------------------------------------------------------------------------------
  485. function ProjectileSystem.DebugProjectiles()
  486.     print("Active Projectiles: " .. #activeProjectiles)
  487.     for i, proj in ipairs(activeProjectiles) do
  488.         ProjectileSystem.PrintProjectileInfo(proj)
  489.     end
  490. end
  491.  
  492. --------------------------------------------------------------------------------
  493. -- Function: GetActiveProjectiles
  494. -- Purpose: Returns the list of all active projectiles.
  495. -- Returns:
  496. --   The activeProjectiles table.
  497. --------------------------------------------------------------------------------
  498. function ProjectileSystem.GetActiveProjectiles()
  499.     return activeProjectiles
  500. end
  501.  
  502. --------------------------------------------------------------------------------
  503. -- Function: ClearAllProjectiles
  504. -- Purpose: Destroys all active projectiles and clears the active list.
  505. --------------------------------------------------------------------------------
  506. function ProjectileSystem.ClearAllProjectiles()
  507.     for i = #activeProjectiles, 1, -1 do
  508.         ProjectileSystem.DestroyProjectile(activeProjectiles[i])
  509.     end
  510. end
  511.  
  512. --------------------------------------------------------------------------------
  513. -- Function: DrawTrajectory
  514. -- Purpose: Creates visual markers along the predicted projectile path for debugging.
  515. -- Parameters:
  516. --   proj (table)   - The projectile object.
  517. --   steps (number) - Number of points to compute (default 20).
  518. --   deltaTime (number) - Time interval per step (default 0.1 seconds).
  519. --------------------------------------------------------------------------------
  520. function ProjectileSystem.DrawTrajectory(proj, steps, deltaTime)
  521.     local trajectory = ProjectileSystem.GetProjectileTrajectory(proj, steps, deltaTime)
  522.     for i, pos in ipairs(trajectory) do
  523.         local marker = Instance.new("Part")
  524.         marker.Size = Vector3.new(0.2, 0.2, 0.2)
  525.         marker.Shape = Enum.PartType.Ball
  526.         marker.CFrame = CFrame.new(pos)
  527.         marker.Anchored = true
  528.         marker.CanCollide = false
  529.         marker.Transparency = 0.5
  530.         marker.Parent = Workspace
  531.         Debris:AddItem(marker, 3)
  532.     end
  533. end
  534.  
  535. --------------------------------------------------------------------------------
  536. -- Additional Complex Methods for the Projectile System
  537. --------------------------------------------------------------------------------
  538.  
  539. --------------------------------------------------------------------------------
  540. -- Function: HomingProjectile
  541. -- Purpose: Fires a projectile that homes in on a target by adjusting its velocity over time.
  542. -- Parameters:
  543. --   origin (Vector3)    - Starting position.
  544. --   direction (Vector3) - Initial firing direction (unit vector).
  545. --   speed (number)      - Initial speed.
  546. --   damage (number)     - Damage to apply upon impact.
  547. --   lifetime (number)   - Projectile lifetime.
  548. --   shooter (Player)    - The player who fired the projectile.
  549. --   target (Model)      - The target character model.
  550. --------------------------------------------------------------------------------
  551. function ProjectileSystem.HomingProjectile(origin, direction, speed, damage, lifetime, shooter, target)
  552.     local proj = ProjectileSystem.FireProjectile(origin, direction, speed, damage, lifetime, shooter)
  553.     local homingFactor = 5
  554.     local connection
  555.     connection = RunService.Heartbeat:Connect(function(deltaTime)
  556.         if not proj.Part or not proj.Part.Parent then
  557.             connection:Disconnect()
  558.             return
  559.         end
  560.         if target and target.Character and target.Character:FindFirstChild("HumanoidRootPart") then
  561.             local targetPos = target.Character.HumanoidRootPart.Position
  562.             local desiredDir = (targetPos - proj.Position).Unit
  563.             proj.Velocity = proj.Velocity:Lerp(desiredDir * proj.Velocity.Magnitude, homingFactor * deltaTime)
  564.         else
  565.             connection:Disconnect()
  566.         end
  567.     end)
  568.     return proj
  569. end
  570.  
  571. --------------------------------------------------------------------------------
  572. -- Function: SetTrailEffect
  573. -- Purpose: Attaches a particle trail to the projectile.
  574. -- Parameters:
  575. --   proj (table)     - The projectile object.
  576. --   color (Color3)   - The trail color (default white).
  577. --   lifetime (number)- Particle lifetime (default 1 second).
  578. --------------------------------------------------------------------------------
  579. function ProjectileSystem.SetTrailEffect(proj, color, lifetime)
  580.     if proj and proj.Part then
  581.         local emitter = Instance.new("ParticleEmitter")
  582.         emitter.Color = ColorSequence.new(color or Color3.new(1,1,1))
  583.         emitter.Lifetime = NumberRange.new(lifetime or 1)
  584.         emitter.Rate = 50
  585.         emitter.Speed = NumberRange.new(2, 5)
  586.         emitter.Parent = proj.Part
  587.         proj.TrailEmitter = emitter
  588.     end
  589. end
  590.  
  591. --------------------------------------------------------------------------------
  592. -- Function: ExplodeProjectile
  593. -- Purpose: Forces a projectile to explode, triggering an explosion effect and area damage.
  594. -- Parameters:
  595. --   proj (table)         - The projectile object.
  596. --   blastRadius (number) - Explosion radius (default 5 studs).
  597. --   blastPressure (number)- Explosion pressure (default 50000).
  598. --------------------------------------------------------------------------------
  599. function ProjectileSystem.ExplodeProjectile(proj, blastRadius, blastPressure)
  600.     local explosion = Instance.new("Explosion")
  601.     explosion.Position = proj.Position
  602.     explosion.BlastRadius = blastRadius or 5
  603.     explosion.BlastPressure = blastPressure or 50000
  604.     explosion.Parent = Workspace
  605.     -- Instead of traditional particle effects, trigger our custom hit effect.
  606.     if proj and proj.Part then
  607.         -- If the shooter has a character, apply the hit effect to it (for demonstration).
  608.         if proj.Shooter and proj.Shooter.Character then
  609.             ProjectileSystem.ApplyHitEffect(proj.Shooter.Character, proj.Position, proj)
  610.         end
  611.     end
  612.     ProjectileSystem.DestroyProjectile(proj)
  613. end
  614.  
  615. --------------------------------------------------------------------------------
  616. -- Return the ProjectileSystem table so it can be required in other scripts.
  617. --------------------------------------------------------------------------------
  618. return ProjectileSystem
  619.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement